<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Lucas Náiade's blog]]></title><description><![CDATA[flutter dart programming program coding android ios golang rust c c++]]></description><link>https://lucasnaiade.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 21 Apr 2026 04:10:04 GMT</lastBuildDate><atom:link href="https://lucasnaiade.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Integrando AWS IoT Core com Flutter: modelagem, payloads, QoS e integração]]></title><description><![CDATA[Recentemente, tive uma experiência muito positiva com IoT, mais especificamente com o AWS IoT Core. Participei da implementação em uma aplicação Android desenvolvida com Flutter, onde os dispositivos operam sob diferentes tipos de conexão com a inter...]]></description><link>https://lucasnaiade.dev/integrando-aws-iot-core-com-flutter-modelagem-payloads-qos-e-integracao</link><guid isPermaLink="true">https://lucasnaiade.dev/integrando-aws-iot-core-com-flutter-modelagem-payloads-qos-e-integracao</guid><category><![CDATA[iot]]></category><category><![CDATA[aws iot core]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[golang]]></category><category><![CDATA[aws lambda]]></category><category><![CDATA[iot project]]></category><category><![CDATA[Android]]></category><category><![CDATA[brasil]]></category><dc:creator><![CDATA[Lucas Náiade]]></dc:creator><pubDate>Thu, 15 May 2025 23:02:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/LqKhnDzSF-8/upload/58615fcf629919bf56f8f56153744e37.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recentemente, tive uma experiência muito positiva com IoT, mais especificamente com o AWS IoT Core. Participei da implementação em uma aplicação Android desenvolvida com Flutter, onde os dispositivos operam sob diferentes tipos de conexão com a internet, variando em distância e qualidade de sinal. Abaixo um pouco da minha experiência usando AWS IoT Core + AWS Lambda e um app Flutter.</p>
<h3 id="heading-design-ou-modelagem-de-topicos">Design ou modelagem de tópicos</h3>
<p>É uma experiencia muito legal modelar os tópicos para a sua solução usando IOT. Se você está começando agora, os dispositivos podem assinar e também publicar em um tópico ou vários tópicos ao mesmo tempo. Esses tópicos podem ser modelados para garantir uma lógica hierárquica, assim como também ajudar na reutilização e controle de acessos.<br />Por exemplo, supomos que vamos modelar tópicos para um MDM(Mobile Device Management) onde podemos para cada parceiro, separar vários dispositivos que vão receber e enviar dados via IOT:</p>
<ul>
<li><p><code>parceiro1/grupo1/serialequipamento1/apps</code> - Os dispositivos do Parceiro 1 cadastrados no Grupo 1 podem cada um deles ter uma configuração de APPs permitidos.</p>
</li>
<li><p><code>parceiro1/grupo2/serialequipamento2/location</code> - O dispositivo com serial “serialequipamento2” poderá enviar sua localização de GPS para esse tópico, ou qualquer outro equipamento. Poderíamos também ouvir para alimentar uma base de dados por exemplo.</p>
</li>
<li><p>Usar curingas (<code>+</code>, <code>#</code>):</p>
<ul>
<li><p><code>parceiro1/grupo1/+/location</code> → escuta todos os dispositivos.</p>
</li>
<li><p><code>pareceiro2/#</code> → escuta tudo do parceiro.</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-qos-qualidade-de-servico">QoS - Qualidade de Serviço</h3>
<p>Um ponto importante é que o IoT do AWS não oferece suporte ao QoS 2, o que dificulta o uso em alguns cenários como por exemplo onde é necessário garantir entrega única das mensagens e sua aplicação não consegue descartar as duplicadas que podem acontecer no QoS 1. Se a sua solução não consegue lidar com mensagens duplicadas, o AWS IoT Core pode não ser a melhor opção. Nesses casos, alternativas como o HiveMQ podem ser mais adequadas.</p>
<h3 id="heading-integracao-com-outros-servicos-aws">Integração com outros serviços AWS</h3>
<p>O AWS IoT Core é facilmente integrável a outros serviços da AWS. É possível utilizar desde Lambda e DynamoDB até OpenSearch, entre outros, o que abre um leque enorme de possibilidades. Basta configurar um gatilho (trigger) e a integração está pronta.</p>
<p>Na solução em que participei, integramos o processamento de alguns tópicos usando AWS Lambda, com funções escritas inteiramente em Golang.</p>
<h3 id="heading-modelagem-de-payload">Modelagem de payload</h3>
<p>O IOT funciona bem com qualquer tipo de dado desde que você se atente aos limites de tamanho máximo por mensagem. Minha experiencia se limitou a enviar dados como JSON e Protobuf, onde o envio de Protobuf foi focado no ganho de eficiência e performance para um caso especifico.</p>
<p>Exemplo em JSON de um status report de equipamento:</p>
<ul>
<li><p>Tópico → <code>parceiro1/grupo1/serialequipamento1/status</code></p>
</li>
<li><p>Payload → <code>{"batery": 78, "status": "ativo", "version": "1.2.0", "lat": -23.55, "lon": -46.63, "tempetature": 12.5, "timestamp": 1685423421 }</code></p>
</li>
</ul>
<h3 id="heading-conclusao">Conclusão</h3>
<p>Se você está pensando em começar uma solução com IoT, vale a pena experimentar. É uma alternativa econômica, escalável e inteligente em relação a soluções tradicionais como WebSockets, especialmente no contexto de dispositivos móveis e ambientes com conectividade variável e imprevisível. Com os recursos certos e uma boa modelagem, o IoT pode entregar muita confiabilidade e eficiência para suas aplicações.</p>
]]></content:encoded></item><item><title><![CDATA[Resolvendo problema: Flutter + Android Studio + JDK 21]]></title><description><![CDATA[Se o seu projeto Flutter estiver utilizando uma versão do Gradle inferior à 8, é provável que você enfrente problemas ao usar o Android Studio 2024.2. Isso ocorre porque essa versão do Android Studio agora utiliza, por padrão, o JDK 21.
Para solucion...]]></description><link>https://lucasnaiade.dev/resolvendo-problema-flutter-android-studio-jdk-21</link><guid isPermaLink="true">https://lucasnaiade.dev/resolvendo-problema-flutter-android-studio-jdk-21</guid><category><![CDATA[Flutter]]></category><category><![CDATA[JDK21]]></category><category><![CDATA[Android Studio]]></category><category><![CDATA[Bugs and Errors]]></category><category><![CDATA[bug]]></category><category><![CDATA[gradle]]></category><dc:creator><![CDATA[Lucas Náiade]]></dc:creator><pubDate>Wed, 08 Jan 2025 16:17:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/GGewLGcQD-I/upload/e07247560b7bb9fb351530770c826ba7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Se o seu projeto Flutter estiver utilizando uma versão do Gradle inferior à 8, é provável que você enfrente problemas ao usar o Android Studio 2024.2. Isso ocorre porque essa versão do Android Studio agora utiliza, por padrão, o JDK 21.</p>
<p>Para solucionar esse problema, você tem duas opções:</p>
<ol>
<li><p><strong>Atualizar a versão do Gradle do seu projeto</strong>: Consulte <a target="_blank" href="https://www.victorcarreras.dev/2024/10/the-flutter-and-java-21-mystery-in.html">este guia</a> para realizar a atualização.</p>
</li>
<li><p><strong>Reverter para o JDK 17</strong>: Configure o JDK 17 no Flutter com os seguintes comandos:</p>
<p> <strong>Para Unix/Linux/MacOS</strong>:<br /> <code>flutter config --jdk-dir=jdk17path</code><br /> <strong>Para Windows</strong>:<br /> <code>flutter config --jdk-dir "jdk17path"</code></p>
</li>
</ol>
<p>Certifique-se de substituir <code>jdk17path</code> pelo caminho correto do JDK 17 no seu sistema.</p>
]]></content:encoded></item><item><title><![CDATA[jnigen e pigeon: olá nativo]]></title><description><![CDATA[Você já enfrentou o desafio de fazer um aplicativo Flutter usar recursos nativos? Muitas vezes, isso pode ser um tanto traumático. Certa vez, precisei fazer com que um aplicativo usando React Native + Expo utilizasse o GPS em um dispositivo de pagame...]]></description><link>https://lucasnaiade.dev/jnigen-e-pigeonola-nativo</link><guid isPermaLink="true">https://lucasnaiade.dev/jnigen-e-pigeonola-nativo</guid><category><![CDATA[Flutter]]></category><category><![CDATA[jni]]></category><category><![CDATA[pigeon]]></category><category><![CDATA[native]]></category><category><![CDATA[brazil]]></category><category><![CDATA[portuguese]]></category><dc:creator><![CDATA[Lucas Náiade]]></dc:creator><pubDate>Mon, 01 Apr 2024 18:15:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1711977931933/86a1da86-95a7-4090-b633-49df30ef3962.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Você já enfrentou o desafio de fazer um aplicativo Flutter usar recursos nativos? Muitas vezes, isso pode ser um tanto traumático. Certa vez, precisei fazer com que um aplicativo usando <strong><em>React Native + Expo</em></strong> utilizasse o GPS em um dispositivo de pagamentos com Android, e isso foi uma experiência muito bacana. Porém, fora da minha stack habitual, isso me deixou com receios. Mas, afinal, o que é um desenvolvedor sem desafios, não é mesmo?</p>
<p>No Flutter, estou tendo essa experiência e estou achando fantástica. Para quem não conhece o mercado de meio de pagamentos, na maioria dos casos, temos dispositivos Smart POS com versões Android um tanto quanto parecidas com o Axl Rose - com bastante idade, mas que ainda fazem um bom rock. Também temos dispositivos que usam um smartphone como ponte de pagamento, usando Bluetooth (tão amados pinpads).</p>
<p>Junto com os Smart POS, temos as bibliotecas das fabricantes do equipamento que possibilitam o uso da impressora interna, GPS, etc. Além disso, temos as bibliotecas para que possamos transacionar por uma adquirente (fazendo sub). Em sua maior parte, não temos bibliotecas oficiais em Flutter, por exemplo. Daí que começamos a saga.</p>
<h3 id="heading-jnigen-e-jni-da-nova-geracao">Jnigen e Jni da nova geração</h3>
<p><a target="_blank" href="https://pub.dev/packages/jnigen">Jnigen</a> nada mais é do que um gerador das ligações do <a target="_blank" href="https://pub.dev/packages/jni">JNI</a>, o que facilita muito! Me lembrou muito o java2op do Delphi 11 (para Android, sim, eu também tive essa experiência). Para cada classe, temos um wrapper - <code>JString</code>, <code>JInt</code>, etc. Podemos separar nas configurações quais classes vamos gerar, o método de geração e mais. O desenvolvimento tem muito menos atritos e flui com mais velocidade.</p>
<p>Mas nem tudo são flores com o <code>jnigen</code>: apesar de evoluir a passos largos até a data desta postagem, ainda é considerado como "experimental", então não espere que funcione corretamente em 100% dos casos. Eu e meus "comparsas" de desenvolvimento tivemos um "crash" (Segmentation fault (11)) com dispositivos mais antigos rodando API level 23(foi corrigido <a target="_blank" href="https://github.com/dart-lang/native/pull/1163">aqui</a>). Nos Androids mais novos, ficou supimpa!</p>
<h3 id="heading-pigeon-e-os-caminhos-sagrados">Pigeon e os caminhos sagrados</h3>
<p>Com <a target="_blank" href="https://pub.dev/packages/pigeon">Pigeon</a>, o caminho é um pouco diferente, pois ele já usa o clássico "method channel". Existem algumas deficiências, como, por exemplo, não lidar com herança no código a ser gerado e talvez ficar um pouco confuso para você escolher entre <code>FlutterApi</code> ou <code>HostApi</code>. Você escreve o código Dart e, a partir daí, ele gera as conexões, mas sem <code>mixins</code>, <code>interfaces</code> e etc.</p>
<p>O lado bom é que você estará usando um método de comunicação com o código nativo muito mais testado, amplamente usado e estável.</p>
<p>Hoje, eu e meus comparsas partimos de <code>Pigeon</code>, mas continuamos de olho no <code>Jnigen</code>.</p>
]]></content:encoded></item><item><title><![CDATA[Microapps architecture com Dart/Flutter]]></title><description><![CDATA[A arquitetura de microapps tem como objetivo proporcionar ao usuário uma experiência unificada, utilizando diversos microapps altamente especializados.
Com Flutter/Dart, temos a oportunidade ideal para criar projetos com essa arquitetura, devido à co...]]></description><link>https://lucasnaiade.dev/microapps-architecture-com-dartflutter</link><guid isPermaLink="true">https://lucasnaiade.dev/microapps-architecture-com-dartflutter</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[programação]]></category><category><![CDATA[desenvolvedor]]></category><category><![CDATA[brazil]]></category><dc:creator><![CDATA[Lucas Náiade]]></dc:creator><pubDate>Mon, 24 Apr 2023 21:00:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/92dgYPsir9k/upload/v1661959371521/pv0q1wB2U.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A arquitetura de microapps tem como objetivo proporcionar ao usuário uma experiência unificada, utilizando diversos microapps altamente especializados.</p>
<p>Com Flutter/Dart, temos a oportunidade ideal para criar projetos com essa arquitetura, devido à comunidade madura e ao excelente sistema de gerenciamento de pacotes e dependências disponível.</p>
<h1 id="heading-modularizar">Modularizar 🧩</h1>
<p>Ao pensar em microapps, é importante modularizar e especializar partes de um aplicativo maior. Para agilizar o desenvolvimento, é necessário buscar ferramentas que possam ajudar nesse processo.</p>
<p>Uma opção para modularização é o Flutter Modular, que é amplamente conhecido e robusto. Ele permite a modularização e oferece um sistema de Injeção de Dependência (DI) interessante, sem que seja necessário se preocupar com rotas. Outra opção é o Module Provider, que também é bastante robusto e ajuda a modularizar, a utilizar DI e a navegar entre as telas.</p>
<p>Mesmo que existam esses e outros pacotes para ajudar na modularização, em muitas situações pode ser necessário criar nossa própria solução para alcançar esse objetivo. Eu mesmo já gastei algumas horas desenvolvendo algo semelhante ao Flutter Modular e ao Module Provider, construindo uma fachada para o Go_router e um sistema de DI semelhante ao Kiwi ou ao Get_it (que, sim, funcionou! 🤣).</p>
<h1 id="heading-varios-microapps-um-build">Vários microapps, um build 🔗</h1>
<p>Ao contrário da arquitetura de micro serviços, a arquitetura de microapps muitas vezes requer que vários microapps sejam agregados em um único super app, que pode ser construido e distribuido em um único arquivo. Para isso, pode ser necessário ter um projeto ou pacote que sirva como "cola", contendo todas as dependências dos microapps e serviços e, talvez, inicializando o MaterialApp e outras configurações comuns no "void main()".</p>
<p>Com essa abordagem, é possível versionar cada microapp e distribuí-los para diferentes equipes que cuidam de contextos diferentes. Onde trabalho hoje, optamos por um projeto monorepo e gerenciamos as versões usando tags no Git, mas estamos avaliando se precisamos criar um <strong>pub.dev</strong> personalizado (como o <a target="_blank" href="https://pub.dev/packages/unpub">https://pub.dev/packages/unpub</a> ou jFrog, por exemplo) para nossas necessidades específicas.</p>
<blockquote>
<p>Uma vantagem de versionar seus microapps é que um rollback seria muito mais rápido e fácil. Em teoria é só fazer downgrade da versão do microapp nas depedencias dentro do seu projeto "Build".</p>
</blockquote>
<h1 id="heading-dependencias-e-resolucao-de-dependencias">Dependências e resolução de dependências 🤝</h1>
<p>Muitas pessoas optam por criar um pacote somente para gerenciar as versões de dependências globalmente, chamado de "shared_dependencies" na maioria das vezes. Todas as dependências comuns ficariam dentro deste pacote e seriam compartilhadas por todos os microapps.</p>
<p>Embora seja uma proposta interessante, na verdade é desnecessária. O próprio pub tool do Dart faz esse papel com maestria, como podemos ver em exemplos aqui e aqui.</p>
<p>Uma grande vantagem dessa abordagem arquitetural é que cada microapp pode ter suas próprias dependências. Por exemplo, um microapp pode usar somente o ValueNotifier/ChangeNotifier, enquanto outro pode usar MobX. No final, o pub tool resolve todas as dependências de forma eficiente e inteligente.</p>
<h1 id="heading-como-os-microapps-se-comunicam">Como os microapps se comunicam? 📢</h1>
<p>Aqui talvez tenha um ponto critico em sua arquitetura de microapps. Dependendo do ponto em que se quer chegar vários microapps podem ou não trocar dados, e como nós fazemos isso?</p>
<p>Uma das alternativas e uma das mais usadas é com Streams, talvez até usando o já conhecido EventBus, outra é usar um estado compartilhado encapsulado em um package separado, ou para ser mais simples também podemos enviar argumentos diretamente nas rotas, porém uma infinidade de alternativas podem ser desenvolvidas além destas.</p>
<p>Um ponto a ser destacado é que com Flutter Web dependendo da abordagem pretendida, a passagem de parâmetros via rotas(direto na URL) é muito melhor quando uma URL fica exposta ao publico ou pode ser compartilhada, com streams sua camada de apresentação e todo resto pode quebrar em algum momento.</p>
<h1 id="heading-algumas-vantagens-escondidas">Algumas vantagens escondidas 🛸</h1>
<p>Embora muitos desenvolvedores tenham reclamado sobre o build_runner, eu também já tive minhas dificuldades com ele. No entanto, minha experiência com a arquitetura de microapps foi bastante positiva. É possível executá-lo isoladamente em cada microapp, que teoricamente é um pacote diferente, tornando-o muito mais rápido e eficiente.</p>
<p>Outra vantagem dessa arquitetura é a possibilidade de separar a lógica de negócios e os serviços da camada de apresentação. Por exemplo, podemos ter um pacote especializado para consumir uma API e conter as regras de negócio, enquanto outro microapp contém a camada de apresentação e suas reatividades.</p>
]]></content:encoded></item><item><title><![CDATA[Flutter + Golang : código nativo direto da fonte]]></title><description><![CDATA[Introdução
Depois de contribuir para um package da comunidade(por ex. flutter_zxing) tive a curiosidade de me aprofundar mais em como chamar código nativo direto de um projeto Dart, e também estou tendo cada vez mais interesses em linguagens como Rus...]]></description><link>https://lucasnaiade.dev/flutter-golang-codigo-nativo-direto-da-fonte</link><guid isPermaLink="true">https://lucasnaiade.dev/flutter-golang-codigo-nativo-direto-da-fonte</guid><category><![CDATA[FFI]]></category><category><![CDATA[dartffi]]></category><category><![CDATA[golang]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><dc:creator><![CDATA[Lucas Náiade]]></dc:creator><pubDate>Mon, 07 Nov 2022 12:11:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1667588923515/15xbyirwh.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introducao">Introdução</h1>
<p>Depois de contribuir para um package da comunidade<em>(por ex. flutter_zxing)</em> tive a curiosidade de me aprofundar mais em como chamar código nativo direto de um projeto Dart, e também estou tendo cada vez mais interesses em linguagens como Rust e Golang, elas me atraem como o mel atraem ursos 🍯.</p>
<p>Então me propus a usar Golang interpolado com Dart e trouxe aqui um pequeno guia para você desenvolvedor e sofredor.</p>
<p>Antes de tudo é bom ter o Golang corretamente configurado em seu ambiente de desenvolvimento, junto também o C.</p>
<h1 id="heading-golang-com-dart">Golang com Dart 🤝</h1>
<p><a target="_blank" href="https://github.com/lucasnsa/flutterandgolang">Escrevi um código Golang</a> básico que calcula a sequência fibonacci. Note para a importação <code>import "C"</code>, o <a target="_blank" href="https://pkg.go.dev/cmd/cgo">cgo</a> é o responsável pela ajuda de chamar código C com Golang.</p>
<pre><code class="lang-golang"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"C"</span>

<span class="hljs-comment">//export fibonacci</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fibonacci</span><span class="hljs-params">(n C.<span class="hljs-keyword">uint</span>)</span> <span class="hljs-title">C</span>.<span class="hljs-title">uint</span></span> {
    <span class="hljs-keyword">if</span> n &lt;= <span class="hljs-number">1</span> {
        <span class="hljs-keyword">return</span> n
    }
    <span class="hljs-keyword">return</span> fibonacci(n<span class="hljs-number">-1</span>) + fibonacci(n<span class="hljs-number">-2</span>)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {}
</code></pre>
<p>Indicamos a função a ser exportada usando o comentário <code>//export nomefuncao</code> como <a target="_blank" href="https://pkg.go.dev/cmd/cgo#hdr-C_references_to_Go">documentado aqui</a></p>
<p>Para usar o <em>ffigen</em> devemos gerar o <em>build</em> e ter no minimo o arquivo de cabeçalho C e o arquivo de objeto compartilhado, os famosos <code>*.so</code> compilados. Como vou usar o flutter no Android, usei o comando:</p>
<pre><code class="lang-bash">CGO_ENABLED=1 GOOS=<span class="hljs-string">"android"</span> GOARCH=<span class="hljs-string">"amd64"</span> GOARM=<span class="hljs-string">""</span> go build -buildmode=c-shared -o lib.so fibonacci.go
// para ver as possibilidades rode o comando `$ go tool dist list`
</code></pre>
<blockquote>
<p>Talvez você precise apontar para para o NDK adicionando <code>CC=meu caminhoparandk/25.1.8937393/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android21-clang</code></p>
</blockquote>
<p><a target="_blank" href="https://github.com/lucasnsa/flutterandgolang">Criando um projeto Flutter</a> e tendo adicionado o ffigen as dependências de desenvolvimento podemos através do arquivo de cabeçalho gerar nosso código Dart para interpolar tudo isso.</p>
<pre><code class="lang-bash">flutter pub add -d ffigen
</code></pre>
<p>Para configurar o ffigen basta seguir a <a target="_blank" href="https://pub.dev/packages/ffigen">documentação aqui</a>. Nesse nosso projeto adicionamos o seguinte ao final do pubspec.yaml:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Meu sistema é um Linux Fedora</span>
<span class="hljs-attr">ffigen:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">"FibonacciGO"</span>
  <span class="hljs-attr">description:</span> <span class="hljs-string">"Bindings to FibonacciGO"</span>
  <span class="hljs-attr">output:</span> <span class="hljs-string">"lib/generated_bindings.dart"</span>
  <span class="hljs-attr">llvm-path:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">"/usr/local/opt/llvm"</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">'C:\Program Files\llvm'</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">"/usr/lib/llvm-11"</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">"/usr/lib64/libclang.so"</span>
  <span class="hljs-attr">headers:</span>
    <span class="hljs-attr">entry-points:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"lib.h"</span>
</code></pre>
<p>Para gerar os binds basta executar o comando abaixo e o arquivo indicado no <code>pubspec.yaml</code> será gerado</p>
<pre><code class="lang-bash">flutter pub run ffigen
</code></pre>
<p>E para facilitar criei um arquivo para facilitar o <code>bind</code></p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'dart:ffi'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'dart:io'</span>;

<span class="hljs-keyword">import</span> <span class="hljs-string">'package:fluttergo/generated_bindings.dart'</span>;

<span class="hljs-keyword">final</span> FibonacciGO fibonacciGO = FibonacciGO(dylib);

DynamicLibrary _openDynamicLibrary() {
  <span class="hljs-keyword">if</span> (Platform.isAndroid) {
    <span class="hljs-keyword">return</span> DynamicLibrary.open(<span class="hljs-string">'lib.so'</span>);  
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (Platform.isLinux) {
    <span class="hljs-keyword">return</span> DynamicLibrary.open(<span class="hljs-string">'/dev/blog/fluttergo/lib.so'</span>);
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (Platform.isWindows) {
    <span class="hljs-keyword">return</span> DynamicLibrary.open(<span class="hljs-string">'lib.dll'</span>);
  }
  <span class="hljs-keyword">return</span> DynamicLibrary.process();
}

DynamicLibrary dylib = _openDynamicLibrary();
</code></pre>
<blockquote>
<p>Caso tenha gerado os arquivos <code>*.so</code> para Android copie e cole cada um na respectiva pasta da arquitetura</p>
<ul>
<li>android/app/src/main/jniLibs/arm64-v8a</li>
<li>android/app/src/main/jniLibs/armeabi-v7a</li>
<li>android/app/src/main/jniLibs/x86</li>
<li>android/app/src/main/jniLibs/x86_64</li>
</ul>
</blockquote>
<h1 id="heading-performance">Performance 🏇</h1>
<p>Criei uma função com a mesma implementação fibonacci em Dart. Usei o <code>benchmark_harness</code> para medir o desempenho Dart x Golang interpolado obtive o mesmo desempenho.</p>
<pre><code class="lang-dart"><span class="hljs-built_in">int</span> fibonacci(<span class="hljs-built_in">int</span> n) =&gt; n &lt;= <span class="hljs-number">2</span> ? <span class="hljs-number">1</span> : fibonacci(n - <span class="hljs-number">2</span>) + fibonacci(n - <span class="hljs-number">1</span>);
</code></pre>
<p>Porém otimizando o código Golang o <code>trem decolou</code></p>
<pre><code class="lang-golang"><span class="hljs-comment">//export fibonacciSequential</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fibonacciSequential</span><span class="hljs-params">(n C.<span class="hljs-keyword">uint</span>)</span> <span class="hljs-title">C</span>.<span class="hljs-title">uint</span></span> {
    <span class="hljs-keyword">if</span> n &lt;= <span class="hljs-number">1</span> {
        <span class="hljs-keyword">return</span> C.<span class="hljs-keyword">uint</span>(n)
    }

    <span class="hljs-keyword">var</span> n2, n1 C.<span class="hljs-keyword">uint</span> = <span class="hljs-number">0</span>, <span class="hljs-number">1</span>

    <span class="hljs-keyword">for</span> i := C.<span class="hljs-keyword">uint</span>(<span class="hljs-number">2</span>); i &lt; n; i++ {
        n2, n1 = n1, n1+n2
    }

    <span class="hljs-keyword">return</span> n2 + n1
}
</code></pre>
<blockquote>
<p>flutter: Run(RunTime): 388.61892877711654 us. //Dart</p>
<p>flutter: Run(RunTime): 2.5107325 us. //Golang</p>
<p>*µs =&gt; microsegundos</p>
</blockquote>
<p>O código abaixo alcançou <code>flutter: Run(RunTime): 0.3191476466012018 us.</code>. Em contra partida não tentei mais otimizar o código Golang.</p>
<pre><code class="lang-dart"><span class="hljs-built_in">int</span> fibonacciInLoops(<span class="hljs-built_in">int</span> n) {
  <span class="hljs-keyword">if</span> (n &lt; <span class="hljs-number">2</span>) <span class="hljs-keyword">return</span> n;
  <span class="hljs-keyword">var</span> data = [<span class="hljs-number">0</span>, <span class="hljs-number">1</span>];
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">2</span>; i &lt; n + <span class="hljs-number">1</span>; i++) {
    data.add(data[i - <span class="hljs-number">1</span>] + data[i - <span class="hljs-number">2</span>]);
  }
  <span class="hljs-keyword">return</span> data[data.length - <span class="hljs-number">1</span>];
}
</code></pre>
<p>Isso quer dizer que é muito mais provável que somente seja viável usar interpolação caso precise de grande poder computacional, grande volume de dados a ser tratados, algum código nativo especifico ou alguma solução em outra linguagem.</p>
<h1 id="heading-consideracoes">Considerações ✍️</h1>
<p>Caso precise de um poder computacional grande talvez seja necessário interpolar com código nativo usando Golang, Rust, Java ou até C puro - tratamento de imagens e vídeos, criptografia, compressão de arquivos, IA, etc.</p>
<p><a target="_blank" href="https://github.com/lucasnsa/flutterandgolang">Repositório para você conferir</a></p>
<h2 id="heading-fontes">Fontes</h2>
<ul>
<li><a target="_blank" href="https://blog.abelotech.com/posts/fibonacci-numbers-golang/">High-performance Fibonacci numbers generator in Go</a></li>
<li><a target="_blank" href="https://levelup.gitconnected.com/coding-problem-nth-fibonacci-number-in-golang-68db104e318">Coding Problem: Nth Fibonacci Number in Golang by Israel Miles</a></li>
<li><a target="_blank" href="https://sof9816.medium.com/faster-fibonacci-efde16a7d312">Faster Fibonacci by Mustafa Naser</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Genéricos no Dart/Flutter: Arranhando a superfície 🌶]]></title><description><![CDATA[Um tipo genérico é basicamente um modelo de dado que pode receber outros tipos de dados sem se importar com seu tipo. 
O Dart é por padrão uma linguagem type safe, no final sempre é garantido que o dado seja adequado ao seu tipo, aqui que entram os t...]]></description><link>https://lucasnaiade.dev/genericos-no-dartflutter-arranhando-a-superficie</link><guid isPermaLink="true">https://lucasnaiade.dev/genericos-no-dartflutter-arranhando-a-superficie</guid><category><![CDATA[Dart]]></category><category><![CDATA[#dart language]]></category><category><![CDATA[#dart-for-beginners]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[brazilianportuguese]]></category><dc:creator><![CDATA[Lucas Náiade]]></dc:creator><pubDate>Thu, 11 Aug 2022 11:18:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/sAQZbYwts0w/upload/v1660146806540/eu-A3kYTK.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Um tipo genérico é basicamente um modelo de dado que pode receber outros tipos de dados sem se importar com seu tipo. </p>
<p>O Dart é por padrão uma linguagem <code>type safe</code>, no final sempre é garantido que o dado seja adequado ao seu tipo, aqui que entram os tipos genéricos garantindo que você possa ter um código reutilizável com menor duplicação ou segurança na tipagem caso precise.</p>
<blockquote>
<p> Tudo no Dart vem de object mas nem todo <code>Object</code> recebe outro <code>Object</code>. 
 <code>Dynamic</code> é tudo desde que se descubra quem ele é.
 P<em>t</em> merd4... 🤣</p>
</blockquote>
<h1 id="heading-object">Object 🤸‍♂️</h1>
<p>Não é bem um tipo genérico mas a classe Odin -&gt; <code>pai de todos</code>. </p>
<p>A classe Object é uma superclasse onde tudo no Dart deriva(menos null), em muitos casos usa-se Object para inferir que um tipo genérico não possa ser <code>null</code>.</p>
<pre><code class="lang-dart">  <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Catiorro</span>&lt;<span class="hljs-title">T</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Object</span>&gt; </span>{} <span class="hljs-comment">// T não pode ser null</span>
  <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bichano</span>&lt;<span class="hljs-title">T</span>&gt; </span>{} <span class="hljs-comment">// T pode ser null</span>
</code></pre>
<p>Para fazer alguma operação em um tipo Object que referencia um outro tipo, só se dá através de casting.</p>
<pre><code class="lang-dart">  <span class="hljs-built_in">Object</span> inteiro = <span class="hljs-number">1</span>;
  inteiro = inteiro + <span class="hljs-number">1</span>; <span class="hljs-comment">// aqui temos um erro;</span>
  inteiro = (inteiro <span class="hljs-keyword">as</span> <span class="hljs-built_in">int</span>) + <span class="hljs-number">1</span>; <span class="hljs-comment">// já aqui não chora na neneca;</span>
  <span class="hljs-built_in">print</span>(inteiro.runtimeType.toString()); <span class="hljs-comment">// por coincidência em tempo de execução é int, legal? 🧐</span>
</code></pre>
<p>A classe Object quase não tem membros em sua composição e o casting deve ser obrigatório como por exemplo no codiguinho acima.</p>
<h1 id="heading-dynamic">dynamic 🦸</h1>
<p>Já o nosso amigo <code>dynamic</code> não esquenta com nada, mas em alguns casos pode se tornar menos seguro por que ele não reflete tudo.</p>
<p>Por padrão genéricos são de fato dynamics, então em um <code>Catiorro&lt;K, V&gt;</code> K e V são dynamics a não ser que tenha alguma inferência <code>Catiorro&lt;K extends Neneca, V extends String&gt;</code></p>
<pre><code class="lang-dart">  <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Neneca</span></span>{}
  <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Catiorro</span>&lt;<span class="hljs-title">K</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Neneca</span>, <span class="hljs-title">V</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">String</span>&gt;</span>{}
</code></pre>
<pre><code class="lang-dart">   <span class="hljs-built_in">dynamic</span> inteiro = <span class="hljs-number">1</span>;
   inteiro = inteiro + <span class="hljs-number">1</span>;
   inteiro.chorandoNaNeneca(); <span class="hljs-comment">// nem tudo são flores</span>
</code></pre>
<p>O Object te força ao casting inevitável já o dynamic não esquenta com a cabeça e por isso é recomendável que se analise o uso em seu código - Forçar o casting ou não? Sempre vai depender das suas abordagens</p>
<h1 id="heading-velocidade">Velocidade ⌛</h1>
<p>Em algumas medições usando o package <a target="_blank" href="https://pub.dev/packages/benchmark_harness">benchmark_harness</a> fica evidente uma vantagem do <code>dynamic</code> para dados em listas.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:benchmark_harness/benchmark_harness.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ListBenchmark</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">BenchmarkBase</span> </span>{
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">String</span> listName;
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">dynamic</span> list;
  <span class="hljs-keyword">const</span> ListBenchmark(<span class="hljs-keyword">this</span>.list, <span class="hljs-keyword">this</span>.listName) : <span class="hljs-keyword">super</span>(listName);

  <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> main(<span class="hljs-built_in">dynamic</span> list, <span class="hljs-built_in">String</span> listName) {
    ListBenchmark(list, listName).report();
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> run() {
    list.sort();
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> setup() {}

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> teardown() {}

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> exercise() =&gt; run();
}

<span class="hljs-keyword">void</span> main() {
  <span class="hljs-keyword">final</span> listDynamic = &lt;<span class="hljs-built_in">dynamic</span>&gt;[<span class="hljs-number">5</span>, <span class="hljs-number">9</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">-11</span>, <span class="hljs-number">10</span>, <span class="hljs-number">300</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">9</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">-11</span>, <span class="hljs-number">10</span>, <span class="hljs-number">300</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">9</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">-11</span>, <span class="hljs-number">10</span>, <span class="hljs-number">300</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>];
  <span class="hljs-keyword">final</span> listInt = &lt;<span class="hljs-built_in">int</span>&gt;[<span class="hljs-number">5</span>, <span class="hljs-number">9</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">-11</span>, <span class="hljs-number">10</span>, <span class="hljs-number">300</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">9</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">-11</span>, <span class="hljs-number">10</span>, <span class="hljs-number">300</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">9</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">-11</span>, <span class="hljs-number">10</span>, <span class="hljs-number">300</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>];
  <span class="hljs-keyword">final</span> listObject = &lt;<span class="hljs-built_in">Object</span>&gt;[<span class="hljs-number">5</span>, <span class="hljs-number">9</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">-11</span>, <span class="hljs-number">10</span>, <span class="hljs-number">300</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">9</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">-11</span>, <span class="hljs-number">10</span>, <span class="hljs-number">300</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">9</span>, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>, <span class="hljs-number">-11</span>, <span class="hljs-number">10</span>, <span class="hljs-number">300</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>];
  <span class="hljs-comment">// Run Benchmark</span>
  ListBenchmark.main(listDynamic, <span class="hljs-string">'Lista com dynamic'</span>);
  ListBenchmark.main(listInt, <span class="hljs-string">'Lista tipada com int'</span>);
  ListBenchmark.main(listObject, <span class="hljs-string">'Lista tipada com Object'</span>);
}
</code></pre>
<p>Resultou em algo que talvez te deixe confuso mas que não tem impacto real dependendo da sua aplicação.</p>
<pre><code class="lang-bash">Lista com dynamic(RunTime): 0.29372134156476754 us.
Lista tipada com int(RunTime): 0.3137372864007163 us.
Lista tipada com Object(RunTime): 0.3100669393288707 us.
Exited
</code></pre>
<blockquote>
<p><code>*µs =&gt; microsegundos</code></p>
</blockquote>
<h1 id="heading-legalzinho-de-ver">Legalzinho de ver 🌶</h1>
<p>Meu amigo <a target="_blank" href="https://kmartins.dev/">kmartins.dev</a> pediu uma ajuda para lançar uma exceção caso um campo seja null sem precisar usar o <code>Null assertion operator</code>, parecido com o <code>getOrThrow do Kotlin</code>.</p>
<p>Em conjunto chegamos em uma solução usando genéricos:</p>
<pre><code class="lang-dart">  X getOrThrow&lt;X&gt;(X? x) =&gt; x ?? (<span class="hljs-keyword">throw</span> Exception()); 🤔
</code></pre>
<p>Esquisito ou não? 😵‍💫</p>
<p>Com isso o <a target="_blank" href="https://kmartins.dev/">kmartins.dev</a> chegou na seguinte extension para Dart/Flutter</p>
<pre><code class="lang-dart"><span class="hljs-keyword">extension</span> ObjectExt&lt;T&gt; <span class="hljs-keyword">on</span> T?{
  T getOrThrow() =&gt; <span class="hljs-keyword">this</span> ?? (<span class="hljs-keyword">throw</span> Exception());
  T? getOrNull() =&gt; <span class="hljs-keyword">this</span>;
}
</code></pre>
<p> Com isso temos:</p>
<pre><code class="lang-dart">  <span class="hljs-keyword">final</span> <span class="hljs-built_in">int?</span> valor = <span class="hljs-number">2</span>;
  <span class="hljs-keyword">final</span> receber= valor.getOrThrow();
  <span class="hljs-keyword">final</span> naoReceber = valor.getOrNull();
</code></pre>
<p>Como diria o Xaropinho, <code>rapaaaaaaaaaaizzzzzzzzz</code> 🐀</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://media.giphy.com/media/TgMy2CyKh8y9Na4fHz/giphy.gif">https://media.giphy.com/media/TgMy2CyKh8y9Na4fHz/giphy.gif</a></div>
<p><strong>(Dá like ae!!!!!)</strong></p>
<h4 id="heading-fontes">Fontes</h4>
<ul>
<li>https://dart.dev/guides/language/language-tour#generics</li>
<li>https://dart.dev/guides/language/type-system</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Groveman: The best logging for Dart/Flutter applications 🐛]]></title><description><![CDATA[Sabemos que nenhum desenvolvedor Flutter vive apenas de debugPrint(não em teoria) e que dentro deste universo de packages temos os tão amados packages de logging e cada um deles com suas peculiaridades. 
De modo geral muitos destes packages vão além ...]]></description><link>https://lucasnaiade.dev/groveman-the-best-logging-for-dartflutter-applications</link><guid isPermaLink="true">https://lucasnaiade.dev/groveman-the-best-logging-for-dartflutter-applications</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[logging]]></category><category><![CDATA[observability]]></category><category><![CDATA[o11y]]></category><dc:creator><![CDATA[Lucas Náiade]]></dc:creator><pubDate>Thu, 21 Jul 2022 17:08:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/UuX-Le3bfzc/upload/v1658418108561/0rhZ9sJ68.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sabemos que nenhum desenvolvedor Flutter vive apenas de <em>debugPrint</em><em>(não em teoria)</em> e que dentro deste universo de packages temos os tão amados packages de logging e cada um deles com suas peculiaridades. </p>
<p>De modo geral muitos destes packages vão além da função de ajudar no <strong>debug e mostrar logs de forma legível e organizada,</strong> chegam até ao ponto de <strong>ajudar em adicionar observabilidade e contexto em aplicativos já em produção</strong>. Desta vez vou apresentar o package <a target="_blank" href="https://pub.dev/packages/groveman"><strong>Groveman</strong></a>.</p>
<h1 id="heading-na-voz-do-dev">Na voz do dev</h1>
<p>Fiz três perguntas ao <a target="_blank" href="https://kmartins.dev/">Kauê Martins</a>, mantenedor e criador do package <strong>Groveman</strong> e ele dá um panorama de pôr qual razão o Groveman existe.</p>
<blockquote>
<blockquote>
<blockquote>
<p>O Groveman basicamente é uma versão do Timber do ecossistema Android só que para Flutter, que visa melhorar o gerenciando dos logs de uma aplicação, cito como exemplo, a possibilidade de exibir determinados logs de acordo com o flavor do seu aplicativo.Groveman (Homem do bosque) é pra passar a ideia de realmente alguém está plantando uma árvore (Groveman.plantTree).<em>Martins, Kauê; 2022.</em></p>
</blockquote>
</blockquote>
</blockquote>
<h1 id="heading-usando">Usando</h1>
<p>Você pode começar adicionando as dependências do <code>pubspec.yaml</code> ou chamando o cli <code>flutter pub add groveman</code>. Depois disso só basta inicializar o logging. 
Uma coisa bem legal aqui, é que você pode adicionar várias <em>trees</em> e cada uma delas desempenhar um papel diferente.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">void</span> main(){  
    Groveman.plantTree(DebugTree()); <span class="hljs-comment">// Mostra logs somente em debug...</span>
</code></pre>
<p>Podemos usar 5 níveis de logging : <strong>fatal, error, warning, info, debug</strong>. </p>
<pre><code class="lang-dart">Groveman.debug(); 
Groveman.info(<span class="hljs-string">'info'</span>, tag: <span class="hljs-string">'info'</span>); 
Groveman.warning(<span class="hljs-string">'error'</span>, tag: <span class="hljs-string">'info'</span>, 
  extra: &lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">Object</span>&gt;{
     <span class="hljs-string">'name'</span>: <span class="hljs-string">'Jungle'</span>,
     <span class="hljs-string">'trees'</span>: <span class="hljs-number">50</span>,
   }, ); 
Groveman.error(<span class="hljs-string">'info'</span>, tag: <span class="hljs-string">'info'</span>, error: Error());
Groveman.fatal(<span class="hljs-string">'info'</span>, tag: <span class="hljs-string">'info'</span>, stackTrace: StackTrace.current);
</code></pre>
<blockquote>
<blockquote>
<blockquote>
<p>Segundo o readme.md : '<em>There are no Tree implementations installed by default because, second Timber, every time you log in production, a puppy dies.</em>'</p>
</blockquote>
</blockquote>
</blockquote>
<h1 id="heading-estendendo">Estendendo</h1>
<p>Também podemos usar outras <em>Trees</em> e enviar os logs para um serviço como o Sentry por exemplo, esses <em>Trees</em> adicionais são disponibilizadas em packages separados, temos o <a target="_blank" href="https://pub.dev/packages/groveman_sentry">Groveman Sentry</a> e o <a target="_blank" href="https://pub.dev/packages/groveman_crashlytics">Groveman Crashlytics</a>.</p>
<p>Caso em sua stack você use um outro serviço de logging como por exemplo o <strong>ElasticSearch</strong> ou <strong>Datadog</strong> você pode simplesmente implementar sua própria <em>Tree</em> e nela você decide o que é importante ou não.</p>
<h1 id="heading-finalizando">Finalizando</h1>
<p>O package Groveman encara e resolve bem os problemas comuns no desenvolvimento de apps Flutter com uma mensagem legal de consciência social. Não se esqueça, plante arvores. 🌳🌳🌳</p>
]]></content:encoded></item></channel></rss>