<?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" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Adby Santos on Medium]]></title>
        <description><![CDATA[Stories by Adby Santos on Medium]]></description>
        <link>https://medium.com/@adbysantos?source=rss-36b5f6359189------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*HA-j96GU0K5EqRKAgS-nnw.jpeg</url>
            <title>Stories by Adby Santos on Medium</title>
            <link>https://medium.com/@adbysantos?source=rss-36b5f6359189------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 23 May 2026 09:15:47 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@adbysantos/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Dev Flutter Sênior #1.6: O guia definitivo de versionamento de plugins]]></title>
            <link>https://adbysantos.medium.com/dev-flutter-s%C3%AAnior-1-6-o-guia-definitivo-de-versionamento-de-plugins-9d3655db7fc7?source=rss-36b5f6359189------2</link>
            <guid isPermaLink="false">https://medium.com/p/9d3655db7fc7</guid>
            <category><![CDATA[flutter-app-development]]></category>
            <category><![CDATA[flutter-widget]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[app-development]]></category>
            <dc:creator><![CDATA[Adby Santos]]></dc:creator>
            <pubDate>Mon, 08 Sep 2025 15:01:04 GMT</pubDate>
            <atom:updated>2025-09-08T15:01:04.702Z</atom:updated>
            <content:encoded><![CDATA[<p>Nos capítulos anteriores, você aprendeu como transformar funcionalidades em plugins Flutter reutilizáveis.</p><p>Pode ser que eles nunca precisem de manutenção durante todo o ciclo de vida deles. Seria o sonho de todos os envolvidos no projeto, não é?</p><p>Por outro lado, também é válido conceber que bugs podem ocorrer e funcionalidades podem ser acrescentadas nesses plugins, correto?</p><p>Concorda, também, que uma simples correção em uma String tem um peso muito menor do que uma alteração na estrutura de um dos métodos públicos do plugin?</p><p>Acredite: Incontáveis times de desenvolvimento já sofreram por horas tentando encontrar a origem de um problema após simplesmente terem executado o comando flutter pub upgrade.</p><p>A dúvida é: Você, como parte de um time, sentiria-se confortável para atualizar um plugin sem nenhuma informação sobre o que mudou nele?</p><p>Se a sua resposta é não, este capítulo foi feito para você.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-92btp5XG3iwmQ-JL6aBag.png" /><figcaption>Imagem: ChatGPT</figcaption></figure><h3>Por que versionar plugins importa?</h3><p>Imagine o seguinte cenário:</p><ul><li>Você criou um plugin para lidar com reconhecimento facial.</li><li>Seu app funciona perfeitamente com a versão inicial.</li><li>Um tempo depois, você altera a assinatura de um método interno, publica o plugin novamente, mas esquece de atualizar a versão corretamente.</li></ul><p>O que acontece?</p><ul><li>Seu app continua funcionando.</li><li>Mas qualquer outro app que dependa desse plugin pode quebrar instantaneamente.</li></ul><p>Esse é o tipo de problema que <strong>um bom versionamento evita</strong>.<br>Com versionamento semântico bem aplicado, você consegue sinalizar para qualquer desenvolvedor (ou para você mesmo, no futuro) <strong>o impacto real de cada mudança</strong> feita no plugin.</p><h3>Como funciona o versionamento no Flutter/Dart</h3><p>No ecossistema Flutter, cada plugin possui um arquivo pubspec.yaml.<br>É nele que você define o número de versão:</p><pre>version: 0.0.1</pre><p>Mas esse número não é arbitrário. Ele segue o padrão <strong>SemVer (Semantic Versioning)</strong>, que funciona assim:</p><ul><li><strong>MAJOR</strong> (X.0.0) → mudanças que quebram compatibilidade.<br>Exemplo: você altera a assinatura de um método do plugin.</li><li><strong>MINOR</strong> (0.X.0) → novas funcionalidades adicionadas, mas compatíveis com versões anteriores.<br>Exemplo: você adiciona um novo método pause() sem alterar o já existente play().</li><li><strong>PATCH</strong> (0.0.X) → correções de bugs ou ajustes internos que não alteram a API pública.<br>Exemplo: corrigir um bug de null safety em um método.</li></ul><p>Portanto, se o seu plugin está na versão 1.2.3, isso significa:</p><ul><li>1 → já houve pelo menos uma mudança que quebrou compatibilidade.</li><li>2 → novas funcionalidades foram adicionadas desde o último <em>major</em>.</li><li>3 → já foram feitas três correções desde o último <em>minor</em>.</li></ul><h3>Como o Flutter resolve versões de plugins</h3><p>No pubspec.yaml dos <strong>apps que consomem o plugin</strong>, declaramos as dependências assim:</p><pre>dependencies:<br>  flutter_text_to_speech: ^1.2.0</pre><p>O símbolo ^ (caret) significa: <strong>aceitar qualquer atualização que não altere o primeiro número (MAJOR)</strong>.<br>Ou seja, no exemplo acima, o app pode instalar automaticamente qualquer versão entre <strong>1.2.0 e &lt;2.0.0</strong>.</p><p>Esse mecanismo dá flexibilidade para evoluir seu app sem precisar fixar cada versão, mas exige disciplina:</p><ul><li>Se você subir de 1.2.0 para 2.0.0, apps que usam ^1.2.0 não serão atualizados automaticamente (para evitar quebra).</li><li>Se você não respeitar o SemVer e fizer mudanças quebráveis (<em>breaking changes</em>) em versões <em>minor</em> ou <em>patch</em>, os consumidores do plugin vão sofrer.</li></ul><h3>Boas práticas para versionar plugins</h3><ol><li><strong>Sempre use SemVer corretamente</strong></li></ol><ul><li>Mudou assinatura de método? <strong>MAJOR</strong>.</li><li>Adicionou novo método? <strong>MINOR</strong>.</li><li>Corrigiu bug interno? <strong>PATCH</strong>.</li></ul><p><strong>2. Mantenha um CHANGELOG.md</strong></p><ul><li>Documente de forma clara cada alteração feita.</li><li>Isso ajuda você e ajuda times que usam seu plugin.</li></ul><p><strong>3. Evite publicar versões desnecessárias</strong></p><ul><li>Teste antes de publicar.</li><li>Agrupe correções menores em uma única versão sempre que possível.</li></ul><p><strong>4. Comunique as mudanças</strong></p><ul><li>Se o plugin é público, descreva bem o impacto de cada atualização.</li><li>Se o plugin é interno (usado em times), notifique os devs.</li></ul><h3>Exemplo prático</h3><p>Suponha que você começou com:</p><pre>version: 0.0.1</pre><h4>1. Você adicionou um novo método pause()</h4><p>Novo número:</p><pre>version: 0.1.0</pre><h4>2. Você corrigiu um bug em speak()</h4><p>Novo número:</p><pre>version: 0.1.1</pre><h4>3. Você alterou a assinatura do método speak()</h4><p>Novo número:</p><pre>version: 1.0.0</pre><p>Percebe como cada número conta uma história?</p><h3>O impacto real no dia a dia de um Dev Flutter</h3><p>Em grande projetos, <strong>as versões de plugins em produção são sempre travadas</strong> para evitar que mudanças inesperadas quebrem builds. Times maduros só atualizam dependências depois de analisar cuidadosamente o <em>changelog</em>.</p><p>O recado aqui é simples:<br> 👉 Um desenvolvedor Flutter júnior cria plugins.<br> 👉 Um desenvolvedor Flutter sênior cria plugins <strong>que podem ser usados, mantidos e evoluídos com segurança por qualquer time</strong>.</p><p>E é exatamente isso que o versionamento correto proporciona.</p><h3>Conclusão</h3><p>Neste capítulo, você aprendeu:</p><ul><li>Por que versionar plugins é essencial.</li><li>Como funciona o versionamento semântico (SemVer).</li><li>Como o Flutter/Dart resolvem versões em pubspec.yaml.</li><li>Quais são as boas práticas para manter plugins utilizáveis em qualquer cenário.</li></ul><p>Com esse conhecimento, você não só cria plugins, mas garante que eles possam evoluir de forma sustentável, sem gerar dores de cabeça para você ou para outros devs.</p><p>No próximo capítulo, daremos um passo além: <strong>integração de SDKs nativos em apps Flutter</strong>.</p><p>Agora que você sabe criar e manter plugins corretamente, é hora de aprender como usar essa base para conectar apps Flutter com SDKs de terceiros — o que realmente diferencia um profissional sênior no mercado.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9d3655db7fc7" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Dev Flutter Sênior #1.5: Como utilizar bibliotecas iOS por meio de um plugin Flutter.]]></title>
            <link>https://adbysantos.medium.com/dev-flutter-s%C3%AAnior-1-5-como-utilizar-bibliotecas-ios-por-meio-de-um-plugin-flutter-8db13832e9ba?source=rss-36b5f6359189------2</link>
            <guid isPermaLink="false">https://medium.com/p/8db13832e9ba</guid>
            <dc:creator><![CDATA[Adby Santos]]></dc:creator>
            <pubDate>Mon, 01 Sep 2025 15:00:39 GMT</pubDate>
            <atom:updated>2025-09-01T15:00:39.516Z</atom:updated>
            <content:encoded><![CDATA[<p>iOS, o famigerado sistema operacional mobile da senhora Apple, conhecido por ser um ambiente super fechado e restrito.</p><p>Eis que <em>um</em> <em>wild Flutter aparece</em> consumindo o que vê pela frente: Autenticação facial, reprodução de áudio, realidade aumentada… Todo o <strong>poder do iOS</strong> agora a um <a href="https://docs.flutter.dev/dash">Dash</a> de distância.</p><p>No entanto, assim como no Android, o <strong>Flutter não acessa diretamente essas bibliotecas</strong>.</p><p>Você já aprendeu nos capítulos anteriores que a ponte entre o Flutter e o código nativo é o <strong>Method Channel</strong>, e que o plugin é a solução para reaproveitar essa comunicação de forma organizada, simples e reutilizável.</p><p>No capítulo 4, você viu em detalhes como o “lado nativo Android” de um plugin responde às chamadas vindas do Flutter. <br>Agora é a hora de entender como isso funciona <strong>no iOS, com Swift</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vpixKvMrEQVKQuJ4vK6-yw.png" /><figcaption>Imagem: ChatGPT</figcaption></figure><h3>Recapitulando o código Flutter do plugin</h3><p>Antes de mergulhar no Swift, vale relembrar o ponto de partida.<br> O código abaixo representa a classe MethodChannelFlutterTextToSpeech, responsável por chamar o método speak() e enviar uma mensagem ao lado nativo via <strong>Method Channel</strong>:</p><pre>import &#39;package:flutter/foundation.dart&#39;;<br>import &#39;package:flutter/services.dart&#39;;<br>import &#39;flutter_text_to_speech_platform_interface.dart&#39;;<br><br>class MethodChannelFlutterTextToSpeech extends FlutterTextToSpeechPlatform {<br>  @visibleForTesting<br>  final methodChannel = const MethodChannel(&#39;flutter_text_to_speech&#39;);<br>  @override<br>  Future&lt;void&gt; speak(String text) async {<br>    await methodChannel.invokeMethod(&#39;speak&#39;, text);<br>  }<br>}</pre><p>Ou seja:</p><ol><li>O app Flutter chama speak(&quot;Texto para falar&quot;);</li><li>O plugin envia uma chamada via invokeMethod;</li><li>Agora, cabe ao <strong>lado nativo (Android ou iOS)</strong> receber, processar e responder.</li></ol><p>No capítulo anterior, você como isso acontece no Android com Kotlin. Agora, é hora de abrir a caixa-preta do lado <strong>Swift/iOS</strong>.</p><h3>O código Swift do plugin</h3><p>Aqui está a implementação da classe nativa em Swift, responsável por responder às chamadas Flutter no iOS:</p><pre>import Flutter<br>import UIKit<br>import AVFoundation<br><br>public class FlutterTextToSpeechPlugin: NSObject, FlutterPlugin {<br>  private var synthesizer: AVSpeechSynthesizer!<br>  private var textToSpeechChannel: FlutterMethodChannel!<br><br>  public static func register(with registrar: FlutterPluginRegistrar) {<br>    let instance = FlutterTextToSpeechPlugin()<br>    instance.initializeTextToSpeechService()<br>    instance.initializeTextToSpeechChannel(binaryMessenger: registrar.messenger())<br>    registrar.addMethodCallDelegate(instance, channel: instance.textToSpeechChannel)<br>  }<br><br>  private func initializeTextToSpeechService() {<br>    synthesizer = AVSpeechSynthesizer()<br>  }<br><br>  private func initializeTextToSpeechChannel(binaryMessenger: FlutterBinaryMessenger) {<br>    textToSpeechChannel = FlutterMethodChannel(<br>      name: &quot;flutter_text_to_speech&quot;,<br>      binaryMessenger: binaryMessenger<br>    )<br>  }<br><br>  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {<br>    switch call.method {<br>    case &quot;speak&quot;:<br>      let text = call.arguments as? String<br>      let isTextNotEmpty = text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false<br>      if let validText = text, isTextNotEmpty {<br>        let utterance = AVSpeechUtterance(string: validText)<br>        utterance.voice = AVSpeechSynthesisVoice(language: &quot;pt-BR&quot;)<br>        synthesizer?.speak(utterance)<br>        result(nil)<br>      } else {<br>        result(FlutterError(code: &quot;INVALID_ARGUMENT&quot;, message: &quot;O texto passado está vazio ou nulo.&quot;, details: nil))<br>      }<br>    default:<br>      result(FlutterMethodNotImplemented)<br>    }<br>  }<br>}</pre><h3>Explicando em detalhes</h3><p>👉 <strong>Importações</strong></p><ul><li>Flutter conecta o código ao ecossistema Flutter.</li><li>UIKit fornece componentes essenciais do iOS.</li><li>AVFoundation é a biblioteca da Apple responsável por áudio, fala e multimídia.</li></ul><p>👉 <strong>Classe </strong><strong>FlutterTextToSpeechPlugin</strong></p><ul><li>Extende NSObject e implementa FlutterPlugin.</li><li>Mantém dois objetos principais:</li><li>synthesizer: motor de fala do iOS.</li><li>textToSpeechChannel: o canal de comunicação entre Flutter e iOS.</li></ul><p>👉 <strong>register(with:)</strong><br>Esse método é chamado automaticamente quando o plugin é carregado pelo app Flutter.<br>Ele:</p><ol><li>Cria uma instância do plugin.</li><li>Inicializa o serviço de fala (AVSpeechSynthesizer).</li><li>Cria o <strong>Method Channel</strong> com o nome &quot;flutter_text_to_speech&quot;.</li><li>Registra o plugin como responsável por lidar com as chamadas feitas ao canal.</li></ol><p>👉 <strong>initializeTextToSpeechService()</strong><br>Instancia o AVSpeechSynthesizer, que é o objeto nativo do iOS responsável por transformar texto em voz.</p><p>👉 <strong>initializeTextToSpeechChannel()</strong><br>Cria o canal &quot;flutter_text_to_speech&quot;, exatamente o mesmo usado no código Flutter.<br>Esse “nome em comum” é o que conecta os dois mundos.</p><p>👉 <strong>handle()</strong><br>É o “coração” do plugin Swift.<br>Ele intercepta cada chamada vinda do Flutter, verifica qual método foi chamado e responde:</p><ul><li>Se o método for &quot;speak&quot;:</li></ul><ol><li>Lê o texto passado como argumento.</li><li>Valida se ele não é nulo nem vazio.</li><li>Cria um AVSpeechUtterance (objeto que representa a fala).</li><li>Define a voz em <strong>português brasileiro (</strong><strong>pt-BR)</strong>.</li><li>Pede ao synthesizer para falar.</li><li>Retorna nil ao Flutter (ou seja, sucesso sem valor de retorno).</li></ol><ul><li>Se o método não for implementado: Retorna FlutterMethodNotImplemented.</li></ul><p>👉 <strong>Fluxo final</strong></p><ol><li>O app Flutter chama speak(&quot;Olá mundo!&quot;).</li><li>O Flutter envia a chamada via MethodChannel.</li><li>O iOS recebe no método handle().</li><li>O texto é validado e transformado em fala pelo AVSpeechSynthesizer.</li><li>O iPhone/iPad literalmente “fala” o texto em voz alta.</li><li>Flutter recebe a resposta null indicando sucesso.</li></ol><h3>Conclusão</h3><p>Agora você conhece <strong>dois lados nativos de um plugin Flutter</strong>:</p><ul><li>No Android, com Kotlin e TextToSpeech.</li><li>No iOS, com Swift e AVSpeechSynthesizer.</li></ul><p>Ambos trabalham em conjunto com o Flutter via <strong>Method Channel</strong>, permitindo que um simples método Dart (speak()) seja traduzido em código nativo poderoso e reutilizável.</p><p>No próximo capítulo, você iniciará sua jornada pelo mundo dos SDKs nativos. A objetivo final dessa jornada é te tornar capaz de integrar SDKs nativos com sucesso e sem receios.</p><p>Até lá! 🚀</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8db13832e9ba" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Dev Flutter Sênior #1.4: Como utilizar bibliotecas Android por meio de um plugin Flutter.]]></title>
            <link>https://adbysantos.medium.com/dev-flutter-s%C3%AAnior-1-4-como-utilizar-bibliotecas-android-por-meio-de-um-plugin-flutter-c3ff1e741d42?source=rss-36b5f6359189------2</link>
            <guid isPermaLink="false">https://medium.com/p/c3ff1e741d42</guid>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[flutter-app-development]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[android-app-development]]></category>
            <dc:creator><![CDATA[Adby Santos]]></dc:creator>
            <pubDate>Mon, 25 Aug 2025 15:01:49 GMT</pubDate>
            <atom:updated>2025-08-25T15:01:49.792Z</atom:updated>
            <content:encoded><![CDATA[<p>Autenticação facial, autenticação biométrica, reprodução de vídeo, câmera… Todas essas são funcionalidades acessíveis por meio das bibliotecas nativas do Android.</p><p>Você, dev Flutter, mais do que ninguém, sabe que o Flutter não tem acesso direto a essas bibliotecas — ainda. Porém, você também <a href="https://adbysantos.medium.com/dev-flutter-sênior-1-2-como-aproveitar-o-poder-do-kotlin-e-swift-nos-seus-apps-flutter-728cb5f49322">já aprendeu</a> que é possível acessar bibliotecas nativas em um app Flutter por meio de <em>Method Channel</em>, correto?</p><p>O fato é que quase nunca bibliotecas assim são utilizadas por apenas um ou dois apps, mas, sim, milhares. Nesse sentido, é unânime a opinião de que escrever o mesmo código para acessar uma biblioteca nativa milhares de vezes seria — para dizer o mínimo — improdutivo.</p><p><strong>Eis que os plugins surgem como bastiões da reusabilidade.</strong></p><p>No capítulo anterior, você entendeu a função de cada parte de um plugin, assim como o código Dart responsável por requisitar informações às plataformas nativas.</p><p>Neste capítulo, você entenderá o código Kotlin responsável por responder às solicitações feitas pelo código Dart e, dessa forma, possibilitar que um plugin Flutter consuma bibliotecas nativas do Android.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jHKikeUz7U6YnJanurPV3Q.png" /><figcaption>Imagem: ChatGPT</figcaption></figure><p>É hora de recapitular.</p><p>O plugin desenvolvido até o momento contempla a classe MethodChannelFlutterTextToSpeech, cuja responsabilidade é alimentar os apps-clientes do plugin através da comunicação com o nativo via <em>Method Channel</em>.</p><pre>import &#39;package:flutter/foundation.dart&#39;;<br>import &#39;package:flutter/services.dart&#39;;<br><br>import &#39;flutter_text_to_speech_platform_interface.dart&#39;;<br><br>class MethodChannelFlutterTextToSpeech extends FlutterTextToSpeechPlatform {<br>  <br>  @visibleForTesting<br>  final methodChannel = const MethodChannel(&#39;flutter_text_to_speech&#39;);<br><br>  @override<br>  Future&lt;void&gt; speak(String text) async {<br>    await methodChannel.invokeMethod(&#39;speak&#39;, text);<br>  }<br>}</pre><p>Quando o método speak() do plugin é chamado pelo app, o Method Channel envia um sinal para o lado nativo da aplicação.</p><p>O que acontece agora? Quem é responsável por receber e responder essa chamada via Method Channel quando o app roda no Android?</p><p>É isso que este capítulo irá responder, porém, para que a resposta seja satisfatória, é necessário voltar alguns passos.</p><p>Quando o app é inicializado no Android, o Flutter executa uma série de instruções para instalar o plugin.</p><p>Dentre elas, está a identificação da classe Java/Kotlin responsável por lidar com chamadas via Method Channel. No caso deste plugin, tem-se a classe <em>FlutterTextToSpeechPlugin, </em>localizada em android/src/main/kotlin/com/example/flutter_text_to_speech/flutter_text_to_speech_plugin.kt.</p><p>Essa classe implementa duas interfaces: FlutterPlugin e MethodCallHandler.</p><p>A primeira permite que a classe execute instruções na inicialização e finalização do app; A segunda, que a classe seja capaz de interceptar e responder chamadas via Method Channel.</p><pre>package com.example.flutter_text_to_speech<br><br>import android.content.Context<br>import android.speech.tts.TextToSpeech<br>import io.flutter.embedding.engine.plugins.FlutterPlugin<br>import io.flutter.plugin.common.BinaryMessenger<br>import io.flutter.plugin.common.MethodCall<br>import io.flutter.plugin.common.MethodChannel<br>import io.flutter.plugin.common.MethodChannel.MethodCallHandler<br>import io.flutter.plugin.common.MethodChannel.Result<br>import java.util.*<br><br>class FlutterTextToSpeechPlugin: FlutterPlugin, MethodCallHandler {<br>  private lateinit var textToSpeechChannel : MethodChannel<br>  private lateinit var textToSpeechService: TextToSpeech<br><br>  override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {<br>    initializeTextToSpeechChannel(flutterPluginBinding.binaryMessenger)<br>    initializeTextToSpeechService(flutterPluginBinding.applicationContext)<br>  }<br><br>  private fun initializeTextToSpeechChannel(binaryMessenger: BinaryMessenger) {<br>    textToSpeechChannel = MethodChannel(<br>      binaryMessenger, &quot;flutter_text_to_speech&quot;<br>    )<br>    textToSpeechChannel.setMethodCallHandler(this)<br>  }<br><br>  private fun initializeTextToSpeechService(context: Context) {<br>    textToSpeechService = TextToSpeech(context) {<br>      if (it == TextToSpeech.SUCCESS) {<br>        textToSpeechService.language = Locale.forLanguageTag(&quot;BR&quot;)<br>      }<br>    }<br>  }<br><br>  override fun onMethodCall(call: MethodCall, result: Result) {<br>    if (call.method == &quot;speak&quot;) {<br>      val text = call.arguments&lt;String&gt;()<br>      if (text.isNullOrBlank()) {<br>        result.error(<br>          &quot;INVALID_ARGUMENT&quot;,<br>          &quot;O texto passado está vazio ou nulo.&quot;,<br>          null<br>        )<br>        return<br>      }<br>      textToSpeechService.speak(text, TextToSpeech.QUEUE_FLUSH, null, null)<br>      result.success(null)<br>    } else {<br>      result.notImplemented()<br>    }<br>  }<br><br>  override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {<br>    textToSpeechService.shutdown()<br>    textToSpeechChannel.setMethodCallHandler(null)<br>  }<br>}</pre><p>Aqui, por mais óbvio que seja, vale deixar claro: Apesar de escritas em Kotlin, as classes FlutterPlugin e <em>MethodCallHandler</em> são uma parte do framework Flutter que trabalha “por dentro” do código nativo (o famoso termo “embarcado”).</p><p><strong>Essas classes servem como ponte entre o Android e o Flutter.</strong> É um mecanismo nativo do framework desenvolvido para possibilitar que os apps feitos com Flutter se relacionem com código nativo.</p><p>É uma decisão muito bem pensada de design. É quase como se o time Flutter dissesse: “Nós conscientemente queremos que os desenvolvedores consumam código nativo.”</p><p>Esclarecido esse ponto, é hora de voltar ao código do plugin.</p><p>O método responsável por conter as instruções de inicialização do plugin no Android é o onAttachedToEngine(). No caso deste plugin:</p><ul><li>Criação do objeto <em>MethodChannel</em> no código Kotlin, cuja responsabilidade é interceptar e responder solicitações originadas no app Flutter (mais especificamente, chamadas de invokeMethod).</li><li>Criação do objeto <em>TextToSpeech</em>, responsável por fornecer o método que recebe uma String e solicita que o dispositivo a reproduza em áudio.</li></ul><p>Com isso, o app está pronto para funcionar perfeitamente no Android. O fluxo que envolve o MethodChannel é praticamente o mesmo que foi explicado no capítulo 2 desta série.</p><p>Para relembrar:</p><ol><li>Flutter chama o método <em>invokeMethod</em>;</li><li>Nativo intercepta;</li><li>Nativo responde;</li><li>Flutter recebe a resposta.</li></ol><p>Existem três possibilidades de resposta: Sucesso, erro e método não implementado. Recomenda-se o uso do bloco <em>try-catch</em> para interceptar possíveis erros advindos do lado nativo.</p><p>No caso deste plugin, caso o usuário venha a utilizar a funcionalidade de transformar texto em fala, o fluxo bem-sucedido é:</p><ol><li>O app Flutter chama o método speak(&#39;stringParaFalar&#39;) do plugin;</li><li>O plugin, por sua vez, chama o método invokeMethod(&#39;speak&#39;, stringParaFalar)</li><li>O Method Channel Android interceptará a chamada, verificará se é válida, validará a String enviada e solicitará que o objeto <em>TextToSpeech</em> execute o método speak().</li><li>O Method Channel Android envia uma resposta;</li><li>Flutter recebe <em>null</em> como resposta pois não foi necessário passar nenhum valor.</li></ol><p>Por fim, quando o app é finalizado, o método onDetachedFromEngine() é executado para descartar os recursos não mais necessários.</p><p>No próximo capítulo, você entenderá como o plugin Flutter recebe e responde chamadas de um app Flutter instalado no iOS e, dessa forma, como utilizar bibliotecas iOS por meio de um plugin Flutter.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c3ff1e741d42" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Dev Flutter Sênior #1.3: Entenda sobre plugins no Flutter de uma vez por todas.]]></title>
            <link>https://adbysantos.medium.com/dev-flutter-s%C3%AAnior-1-3-entenda-sobre-plugins-no-flutter-de-uma-vez-por-todas-d794ebc298f6?source=rss-36b5f6359189------2</link>
            <guid isPermaLink="false">https://medium.com/p/d794ebc298f6</guid>
            <category><![CDATA[flutter-app-development]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[flutter]]></category>
            <dc:creator><![CDATA[Adby Santos]]></dc:creator>
            <pubDate>Mon, 18 Aug 2025 15:02:00 GMT</pubDate>
            <atom:updated>2025-08-18T15:02:00.283Z</atom:updated>
            <content:encoded><![CDATA[<h3>Dev Flutter Sênior #1.3: Entenda sobre plugins no Flutter de uma vez por todas — Introdução.</h3><p>Você acabou de construir funcionalidades no seu app Flutter utilizando comunicação com as linguagens nativas Kotlin e Swift.</p><p>Eis que um novo app surge e precisa de funcionalidades semelhantes às do seu app. O que fazer? Escrever o mesmo código novamente?</p><p>Nesse sentido, o objetivo desta série sobre plugins é apresentar o passo a passo para transformar a comunicação nativa que o seu app necessita em um recurso independente e reutilizável. Em outras palavras, um <strong>plugin</strong>.</p><p>Nesta primeira parte, o objetivo é explicar o que são plugins, os arquivos Dart de um plugin, e como iniciar a migração de um Method Channel de um app para um plugin.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0L_PHVZtt-ymMTPVPXF9jQ.png" /></figure><h3>1. O que são plugins?</h3><p>Imagine o seguinte cenário:</p><p>Você acabou de integrar autenticação por biometria em um app. Mais especificamente, desenvolveu widgets, regras de negócio, comunicação com nativo e testes.</p><p>O time gostou bastante do seu projeto. O gestor planeja apresentar o seu trabalho em uma reunião geral da empresa, inclusive. Você, claro, está mais do que satisfeito.</p><p>Animado com os resultados do seu app, um dos clientes da empresa também deseja a mesma autenticação por biometria no app dele. Você aceita o trabalho, mas fica em um impasse: “Devo escrever o código novamente?”.</p><h4>Um plugin pode ser a solução do seu problema</h4><p>Lembre-se: Em teoria, sempre que um mesmo recurso for necessário em dois contextos diferentes, <strong>deve-se tornar aquele recurso independente.</strong></p><p>Caso contrário, imagine o tempo perdido para resolver um mesmo bug em dois códigos que poderiam ser um só. Lembre-se do princípio <strong>Don’t repeat yourself (DRY), não se repita</strong>.</p><p>Dentro do mundo Flutter, se esse recurso é um código Dart que pode ser utilizado por diferentes apps, ele é um pacote. Se esse pacote contempla comunicação com nativo, ele é um <strong>plugin</strong>.</p><p><em>Plug</em> significa “conectar”; <em>in</em>, “em”. Em outras palavras, um plugin é um componente que se conecta ao seu app, sem se limitar a ele, e provê funcionalidades reutilizáveis.</p><p>Um plugin, por ainda ser um pacote, também contempla — ou deveria — todos os benefícios de um pacote: <strong>reusabilidade, encapsulamento, separação de responsabilidades, bem como os benefícios da correta aplicação dos princípios do SOLID.</strong></p><p>“Ok, mas e na prática, quais os benefícios de investir tempo em um plugin?”</p><ol><li>Torna desnecessário reescrever todo um mesmo código do zero em outro app;</li><li>Caso um bug ocorra, ele é resolvido em um só lugar;</li><li>Caso o plugin contemple uma nova funcionalidade, todos os apps que dependem dele têm potencial para utilizá-la;</li><li>Acelera o processo de depuração;</li><li>Facilita o teste de diferentes cenários;</li><li>Evita o trabalho de construir um app (muitas vezes pesado) para depurar bugs;</li><li>Promove um ambiente neutro, livre de efeitos colaterais de outras partes dos apps.</li></ol><p><strong>Existem aplicativos que chegam a contemplar mais de 40 pacotes.</strong> Pare um pouco e reflita sobre o tempo e esforço necessários para construir esse app apenas para depurar algumas linhas de código, ou um ou dois arquivos.</p><p>Apesar das semelhanças, cada projeto tem suas especificidades. Antes de implementar qualquer coisa, é essencial verificar o que é particular de cada app-cliente.</p><p>Aqui é hora de colocar os princípios do <strong>SOLID</strong> em campo.</p><p>Informações particulares (e.g. API Key, credenciais etc) devem sempre vir do app que usa o plugin, nunca devem estar fixadas no código deste. Um plugin deve ser sempre agnóstico à cliente — <strong>Princípio Aberto Fechado</strong>.</p><h3><strong>2. Criando um plugin do zero</strong></h3><p>Agora que você entendeu o que é e por que criar plugins, é hora de acompanhar o processo de exportação (ou terceirização) da comunicação nativa de um app Flutter para dentro de um plugin Flutter.</p><p>Tomar-se-á como ponto de partida o app apresentado na parte 2 desta série, cuja principal funcionalidade era transformar texto em fala (em inglês, <em>text to speech</em>) através do consumo de bibliotecas Kotlin/Swift via Method Channel.</p><p>A princípio, o plano é:</p><ol><li>Exportar a comunicação com o nativo (neste caso, os Method Channels) para o plugin;</li><li>Desenvolver métodos públicos no plugin que permitam o consumo das bibliotecas nativas;</li><li>Consumir as bibliotecas nativas por meio do plugin, não mais diretamente via Method Channel.</li></ol><h3>3. O Hello World de um plugin Flutter</h3><p>Para criar um plugin Flutter, utiliza-se um método semelhante ao da criação de um app Flutter.</p><p>Caso você utilize editores de texto robustos ou IDEs, como VSCode e Android Studio, cada um deles tem uma forma de criar plugins via interface de usuário (UI), ou seja, sem utilizar linha de comando.</p><p>Este artigo não vai cobrir todos os métodos de criação de plugins, visto que há muitas ferramentas disponíveis e, por consequência, muitas maneiras diferentes de se criar um plugin.</p><p>Contudo, o método mais recomendado de se criar um plugin, uma vez que dificilmente mudará, é através da linha de comando (e.g. Terminal, CMD, PowerShell etc).</p><p>Caso você esteja acostumado com linha de comando, dirija-se ao diretório dos seus projetos e rode o comando abaixo:</p><p>flutter create -t plugin --platforms=android,ios flutter_text_to_speech</p><p>Esse comando criará um projeto intitulado flutter_text_to_speech, com a estrutura de um plugin, especificamente para as plataformas Android e iOS.</p><p>Agora, abra o projeto no editor de texto/IDE de sua preferência. O resultado será semelhante a este:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/215/1*ppTWgFpSWn5QHDvd7wkRXw.png" /><figcaption>Imagem: Arquivos iniciais do projeto no VSCode</figcaption></figure><p>O primeiro passo foi concluído. <br>O segundo passo é alterar os arquivos do projeto com os códigos necessários para transformar texto em fala.</p><p>Nesse sentido, a ideia é trabalhar em três aspectos do plugin:</p><ol><li>Código Dart;</li><li>Código Kotlin;</li><li>Código Swift.</li></ol><h3>4. O código Dart inicial</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/335/1*L7BmUKBGs_XhgHgq1a5wOw.png" /><figcaption>Imagem: Código Dart gerado pela CLI do Flutter</figcaption></figure><p>Como explicado no capítulo passado, o código Dart é responsável por manter um MethodChannel e solicitar informações das partes nativas, isto é, do Kotlin e Swift.</p><h4>Platform Interface</h4><p>Diferentemente de um app, um plugin trabalha de uma forma um pouco diferente. Como a ideia de um plugin Flutter é “escreva uma vez, use em qualquer plataforma”, o plugin declara uma interface única que será implementada pelas plataformas que desejam servir o app.</p><p>Em outras palavras, a interface do plugin apenas define o que deve ser respondido, não o como deve ser respondido. Neste exemplo, o arquivo responsável pela interface é flutter_text_to_speech_platform_interface.dart.</p><pre>import &#39;package:plugin_platform_interface/plugin_platform_interface.dart&#39;;<br><br>import &#39;flutter_text_to_speech_method_channel.dart&#39;;<br><br>abstract class FlutterTextToSpeechPlatform extends PlatformInterface {<br>  /// Constructs a FlutterTextToSpeechPlatform.<br>  FlutterTextToSpeechPlatform() : super(token: _token);<br><br>  static final Object _token = Object();<br><br>  static FlutterTextToSpeechPlatform _instance = MethodChannelFlutterTextToSpeech();<br><br>  /// The default instance of [FlutterTextToSpeechPlatform] to use.<br>  ///<br>  /// Defaults to [MethodChannelFlutterTextToSpeech].<br>  static FlutterTextToSpeechPlatform get instance =&gt; _instance;<br><br>  /// Platform-specific implementations should set this with their own<br>  /// platform-specific class that extends [FlutterTextToSpeechPlatform] when<br>  /// they register themselves.<br>  static set instance(FlutterTextToSpeechPlatform instance) {<br>    PlatformInterface.verifyToken(instance, _token);<br>    _instance = instance;<br>  }<br><br>  Future&lt;String?&gt; getPlatformVersion() {<br>    throw UnimplementedError(&#39;platformVersion() has not been implemented.&#39;);<br>  }<br>}</pre><p>Antes de tudo, é válido dizer que não é necessário investir muito tempo observando cada linha de código, visto que a estrutura dos plugins, em geral, segue essa mesma linha.</p><p>Os itens que você precisa prestar atenção são:</p><p><strong>A classe contida no arquivo:</strong></p><ul><li>Caso o plugin necessite dar suporte a uma determinada plataforma (e.g. Linux), deverá ser criada uma classe que implemente a classe contida nesse arquivo.</li></ul><p><strong>Os métodos públicos:</strong></p><ul><li>Eles deverão ser implementados pelas subclasses. Neste exemplo, apenas getPlatformVersion().</li></ul><p><strong>O método <em>static set instance:</em></strong></p><ul><li>Esse método é utilizado quando o valor de _instance não consegue alimentar a plataforma que o app está rodando. Ex.: Caso o plugin tenha uma implementação para o Windows, e o valor inicial de _instance funciona apenas para Android e iOS — que é o caso do Method Channel —, o plugin precisa receber um objeto que seja compatível com a plataforma Windows.</li><li>Tal operação é feita pelas classes de implementação de plataforma (i.e. <em>Platform Implementation</em>).</li></ul><h4><strong>App-Facing Class</strong></h4><pre>import &#39;flutter_text_to_speech_platform_interface.dart&#39;;<br><br>class FlutterTextToSpeech {<br>  Future&lt;String?&gt; getPlatformVersion() {<br>    return FlutterTextToSpeechPlatform.instance.getPlatformVersion();<br>  }<br>}</pre><p>Ao observar a classe, nota-se que o método getPlatformVersion() consome a interface ao invés de consumir uma implementação.</p><p>Isso acontece pois o plugin faz uso do Princípio da Inversão de Dependências.</p><p>Assim, o app-cliente pode consumir um único método e, ao mesmo tempo, fazer uso de diferentes implementações, tudo graças ao poder do <strong>Polimorfismo</strong> e da “injeção de dependências” promovida pelo método <em>set instance </em>de <em>Platform Interface</em>.</p><p>A princípio, instance receberá o valor adequado para a plataforma em que o app-cliente do plugin estiver rodando. Na prática, se o app estiver rodando no Windows, o plugin utilizará a implementação adequada.</p><h4>Platform Implementation</h4><p>Por fim, temos a implementação do contrato estabelecido pela interface.</p><p>A princípio, todas as plataformas que consomem este plugin (i.e., Android e iOS) serão alimentadas por meio de uma única classe, a qual utiliza Method Channel. Caso contrário, poderia haver um arquivo específico para cada plataforma o qual poderia conter uma solução totalmente diferente uma da outra.</p><p>Por padrão, a CLI do Flutter gera um arquivo cujo nome é composto pelo nome do plugin + Method Channel. Neste caso, flutter_text_to_speech_method_channel.dart.</p><pre>import &#39;package:flutter/foundation.dart&#39;;<br>import &#39;package:flutter/services.dart&#39;;<br><br>import &#39;flutter_text_to_speech_platform_interface.dart&#39;;<br><br>/// An implementation of [FlutterTextToSpeechPlatform] that uses method channels.<br>class MethodChannelFlutterTextToSpeech extends FlutterTextToSpeechPlatform {<br>  /// The method channel used to interact with the native platform.<br>  @visibleForTesting<br>  final methodChannel = const MethodChannel(&#39;flutter_text_to_speech&#39;);<br><br>  @override<br>  Future&lt;String?&gt; getPlatformVersion() async {<br>    final version = await methodChannel.invokeMethod&lt;String&gt;(&#39;getPlatformVersion&#39;);<br>    return version;<br>  }<br>}</pre><p>Você pode ter observado que, apesar de ser um arquivo de implementação de plataforma, ele não contém a alteração do valor de _instance como descrito no tópico de <em>Platform Interface</em>.</p><p>Essa ausência se deve pelo fato de que todas as plataformas que consomem este plugin são alimentadas por uma só implementação, logo, nunca é necessário “mudar a estratégia”.</p><p>Por fim, para facilitar a visualização da relação entre os arquivos, pode-se considerar a imagem abaixo:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/471/1*f0xjLsV3QCmUkKWxMex27Q.png" /><figcaption>Imagem: Arquitetura básica de um plugin (autoria própria via draw.io)</figcaption></figure><h3><strong>5. Como migrar um Method Channel do app para um plugin</strong></h3><p>Agora é o momento de trazer as implementações contidas no app para o plugin.</p><h4>Platform Interface</h4><p>O primeiro passo é remover o método getPlatformVersion do arquivo de interface e adicionar o método speak:</p><pre>Future&lt;void&gt; speak(String text) async {<br>  throw UnimplementedError(&#39;speak() has not been implemented.&#39;);<br>}</pre><p>Após essa mudança, a ferramenta de edição de código que você utiliza deverá apontar erros no código do plugin.</p><p>Com o contrato atualizado, é hora de alterar o arquivo que consome e o arquivo que implementa a <em>Platform Interface</em>.</p><h4>App-Facing Class</h4><pre>Future&lt;void&gt; speak(String text) {<br>  return FlutterTextToSpeechPlatform.instance.speak(text);<br>}</pre><h4>Platform Implementation</h4><pre>Future&lt;void&gt; speak(String text) async {<br>  await methodChannel.invokeMethod(&#39;speak&#39;, text);<br>}</pre><p>Com isso, finalizamos a exploração do código Dart, entendendo a estrutura e o papel de cada arquivo dentro de um plugin Flutter. Essa compreensão é essencial para que o recurso seja bem organizado, escalável e fácil de manter.</p><p>Na próxima parte da minissérie, vamos mergulhar no lado <strong>nativo</strong>, abordando como configurar e implementar a comunicação em <strong>Kotlin</strong> e, em seguida, em <strong>Swift</strong>. Dessa forma, você terá uma visão completa de como criar um plugin que reúna o melhor dos dois mundos: a praticidade do Flutter com o poder das plataformas nativas.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d794ebc298f6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Dev Flutter Sênior #1.2: Como aproveitar o poder do Kotlin e Swift nos seus apps Flutter]]></title>
            <link>https://adbysantos.medium.com/dev-flutter-s%C3%AAnior-1-2-como-aproveitar-o-poder-do-kotlin-e-swift-nos-seus-apps-flutter-728cb5f49322?source=rss-36b5f6359189------2</link>
            <guid isPermaLink="false">https://medium.com/p/728cb5f49322</guid>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[flutter-app-development]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <dc:creator><![CDATA[Adby Santos]]></dc:creator>
            <pubDate>Mon, 04 Aug 2025 15:01:42 GMT</pubDate>
            <atom:updated>2025-08-04T15:01:42.640Z</atom:updated>
            <content:encoded><![CDATA[<h3>Dev Flutter Sênior #1.2: Como aproveitar o poder do Kotlin e Swift em seus apps Flutter</h3><p>Talvez não fosse necessário falar sobre a comunicação básica, visto que a documentação já cobre isso, porém… <strong>esmiuçar os fundamentos permitirá entender os próximos capítulos com mais clareza</strong>.</p><p>Nesse caso, enxergue este, em específico, como um reforço da comunicação entre Flutter e Kotlin/Swift.</p><p>Este capítulo está dividido em três partes:</p><ol><li>Fundamentos da comunicação entre Flutter e nativo;</li><li>Código necessário para o Flutter chamar o nativo corretamente;</li><li>Código necessário para os nativos responderem às chamadas Flutter.</li></ol><p>Para entender o por que utilizar, ou ainda, por que aprender a utilizar Kotlin e Swift em apps Flutter, recomenda-se a leitura do primeiro capítulo desta série. <a href="https://medium.com/@adbysantos/dev-flutter-sênior-1-1-a-importância-dos-sdks-nativos-na-sua-carreira-83239f6ca3bf">Clique aqui para ler</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*f06U3TPUvMbQh1ANsqMA9Q.png" /><figcaption>Imagem: ChatGPT</figcaption></figure><h4>Um rápido agradecimento :)</h4><p>Antes de tudo, gostaria de agradecer a todos pelo engajamento.</p><p>Se você teve dificuldade para entender algo, comente abaixo. Críticas são muito bem-vindas.</p><p>A ideia é que esta série te incentive a consumir código Kotlin/Swift sem medo e resolva a maior parte das dúvidas sobre o assunto.</p><p>Sem mais delongas, vamos à primeira parte.</p><h3>1 Fundamentos da comunicação entre Flutter e nativo</h3><h3>1. 1 A ponte entre Flutter e nativo</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/580/0*1RpjryZbQk0A_Jpq.png" /><figcaption>Imagem: docs.flutter.dev</figcaption></figure><p>Para criar uma interface de usuário, o Flutter faz uso de suas próprias bibliotecas, como a material e a widgets , as quais foram escritas em Dart.</p><p>Já para usar uma biblioteca nativa (na imagem, uma <em>Platform API</em>), faz-se necessária uma ponte entre o Dart e o nativo. Tal ponte funciona, entre outras coisas, como um tradutor bidirecional.</p><p>Essa ponte pode ser construída a partir de objetos do tipo PlatformChannel , cujo mais conhecido tipo derivado é o MethodChannel. Sendo assim, haveria um objeto MethodChannel do lado Flutter da “ponte”, e um para cada nativo.</p><p>Nesse sentido, a comunicação funciona como transmissão rádio, que só pode ser ouvida/respondida por quem compartilha a mesma frequência; no caso do MethodChannel, do mesmo identificador único.</p><p>Essa ponte possibilita tanto o Flutter usar bibliotecas nativas, quanto o nativo executar métodos do lado Flutter.</p><p>E o melhor de tudo: Não é preciso se limitar a uma única ponte, então use e abuse do Princípio da Responsabilidade Única na hora de criar seus PlatformChannel.</p><h3>1.2 A estrutura fundamental das chamadas ao nativo</h3><p>Antes de continuar para o código, é válido ressaltar a estrutura fundamental de uma chamada ao nativo via MethodChannel.</p><p>Vejamos o método invokeMethod dessa classe. Ele recebe o nome do método e um argumento como parâmetros.</p><pre>// Flutter chama o nativo<br>methodChannel.invokeMethod(&#39;nomeDoMetodo&#39;, args);</pre><p>O nome do método é uma String tanto no Dart como nos nativos. O argumento, por sua vez, pode ser de qualquer tipo primitivo do Dart. Quando o método é chamado, é a vez do nativo trabalhar.</p><p>Em resumo: O trabalho do nativo é:</p><ul><li>Verificar o nome do método;</li><li>Capturar, validar e usar os argumentos, caso hajam;</li><li>Responder com sucesso ou erro.</li></ul><pre>result.success(null)<br>result.notImplemented()<br>result.error(&quot;INVALID_ARGUMENT&quot;, <br>  &quot;O texto passado está vazio ou nulo.&quot;,<br>  null // detalhes adicionais (pode ser um Map, por exemplo)<br>)</pre><p>Em sua essência, a estrutura sempre será essa, independente do tamanho do método, dos detalhes da implementação e da complexidade do código.</p><h3>1.3 Binary Messenger</h3><pre>// Inicialização de um MethodChannel no Android<br>override fun configureFlutterEngine(flutterEngine: FlutterEngine) {<br>    super.configureFlutterEngine(flutterEngine)<br>    val textToSpeechChannel = MethodChannel(<br>      flutterEngine.dartExecutor.binaryMessenger,<br>      &quot;flutter_text_to_speech_channel&quot;<br>    )<br>}</pre><p><strong>Binary Messenger </strong>é uma classe cujo papel pode passar despercebido, mas será possível notá-lo na criação dos MethodChannel nativos.</p><p>Um objeto do tipo BinaryMessenger serve para definir como as informações passadas para os objetos do tipo MethodChannel serão transmitidas adequadamente por seus respectivos destinatários.</p><p>No caso do BinaryMessenger, sua técnica é autoexplicativa: <strong>ele define que as informações serão transformadas em binário</strong>.</p><p>No Flutter, a implementação padrão dessa classe é a DefaultBinaryMessenger, porém é possível desenvolver sua própria classe, adicionar logs, ter mais rastreabilidade. É um assunto que renderia um artigo dedicado.</p><p>Por hora, lembre-se do papel do Binary Messenger na comunicação entre Flutter e nativo. Isso é mais do que o suficiente para seguir os estudos com segurança.</p><h3>2 Como o Flutter faz uma chamada ao nativo</h3><h4>2.1 Criação do MethodChannel</h4><p>Para realizar uma chamada ao nativo do lado Flutter, precisamos criar um MethodChannel antes de tudo.</p><p>A princípio, o construtor dele apenas uma String, que representa seu identificador único. Diferente dos nativos, não é necessário passar um BinaryMessenger como parâmetro no Flutter.</p><h4>2.2 Chamada ao nativo</h4><p>Para fazer uma chamada ao nativo a partir do Flutter, é necessário executar o método invokeMethod do MethodChannel. Como parâmetros, ele recebe o nome do método e argumentos, caso o método nativo precise.</p><p>Vale notar que invokeMethod retorna um objeto do tipo Future&lt;T?&gt;. Isso significa que:</p><ol><li>O tipo de retorno pode ser definido no lado Flutter;</li><li>invokeMethod pode retornar <em>null;</em></li><li>É uma operação assíncrona.</li></ol><p>Exemplo:</p><pre>//Não é possível compilar, pois channelResponse é do tipo String?<br>Future&lt;String&gt; chamaNativo(dynamic args) async {<br>  final channelResponse = await channel.invokeMethod&lt;String&gt;(&#39;nomeDoMetodo&#39;, args);<br>  return channelResponse;<br>}<br><br>Future&lt;String&gt; chamaNativo(dynamic args) async {<br>  final channelResponse = await channel.invokeMethod&lt;String&gt;(&#39;nomeDoMetodo&#39;, args);<br>  if(channelResponse == null){<br>    //Caso seja tratável, trata, <br>    //senão, pode fazer throw<br>    //ou mudar o retorno do método para String?<br>  }<br>  return channelResponse;<br>}</pre><h4>2.3 Exemplo prático: App que transforma texto em fala</h4><p>Para fins de facilitar o entendimento, o código a seguir foi baseado no app de Hello World do Flutter, ou seja, nenhum princípio de arquitetura foi aplicado.</p><pre>import &#39;package:flutter/material.dart&#39;;<br>import &#39;package:flutter/services.dart&#39;;<br><br>void main() {<br>  runApp(const MyApp());<br>}<br><br>class MyApp extends StatelessWidget {<br>  const MyApp({super.key});<br><br>  @override<br>  Widget build(BuildContext context) {<br>    return MaterialApp(<br>      title: &#39;Flutter Demo&#39;,<br>      theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple)),<br>      home: const MyHomePage(title: &#39;Flutter Demo Home Page&#39;),<br>    );<br>  }<br>}<br><br>class MyHomePage extends StatefulWidget {<br>  const MyHomePage({super.key, required this.title});<br><br>  final String title;<br><br>  @override<br>  State&lt;MyHomePage&gt; createState() =&gt; _MyHomePageState();<br>}<br><br>class _MyHomePageState extends State&lt;MyHomePage&gt; {<br>  final methodChannel = MethodChannel(&#39;flutter_text_to_speech_channel&#39;);<br><br><br>  Future&lt;void&gt; execute(Future&lt;void&gt; Function() func) async {<br>    try {<br>      await func();<br>    } on PlatformException catch (e) {<br>      debugPrint(&#39;Erro: ${e.code} - ${e.message}&#39;);<br>    } on MissingPluginException catch (e) {<br>      debugPrint(&#39;Erro: ${e.message}&#39;);<br>    }<br>  }<br><br>  Future&lt;void&gt; channelSpeakCorrectly() async {<br>    await methodChannel.invokeMethod(&#39;speak&#39;, &#39;Olá mundo&#39;);<br>  }<br><br>  Future&lt;void&gt; channelEmptyTextError() async {<br>    await methodChannel.invokeMethod(&#39;speak&#39;, &#39;&#39;);<br>  }<br><br>  Future&lt;void&gt; channelNotImplementedMethodError() async {<br>    await methodChannel.invokeMethod(&#39;sspeak&#39;, &#39;&#39;);<br>  }<br><br>  @override<br>  Widget build(BuildContext context) {<br>    return Scaffold(<br>      appBar: AppBar(title: Text(widget.title)),<br>      body: Center(<br>        child: Column(<br>          mainAxisAlignment: MainAxisAlignment.spaceAround,<br>          children: &lt;Widget&gt;[<br>            IconButton(onPressed: () =&gt; execute(channelSpeakCorrectly), icon: Icon(Icons.check)),<br>            IconButton(onPressed: () =&gt; execute(channelEmptyTextError), icon: Icon(Icons.error)),<br>            IconButton(onPressed: () =&gt; execute(channelNotImplementedMethodError), icon: Icon(Icons.question_mark)),<br>          ],<br>        ),<br>      ),<br>    );<br>  }<br>}</pre><p>Recomenda-se, antes de tudo, investir tempo na compreensão de cada linha desse código antes de utilizá-lo.</p><p>No geral, para toda chamada ao nativo via MethodChannel, as possibilidades de resposta são: sucesso, erro e “método não implementado/inválido”.</p><p>Quando um erro ocorre, os objetos devolvidos (<em>thrown</em>) pelo MethodChannel contém — ou deveriam conter — informações relevantes sobre o erro, o que facilita em casos de bug ou mal funcionamento.</p><p>Sabendo disso, o código acima apresenta três métodos, um para cada tipo possível de retorno:</p><ol><li>Para o caso de sucesso, o dispositivo ditará o texto passado como argumento;</li><li>Para o caso de erro, tem-se que o nativo sinaliza o MethodChannel e este realiza o throw de um objeto de tipo PlatformException, o qual contém informações úteis preenchidas no lado nativo;</li><li>Para o caso de “método não implementado”, o MethodChannel realiza o throw de um objeto de tipo MissingPluginException, semelhante ao PlatformException.</li></ol><h3><strong>3 Como o nativo responde chamadas</strong></h3><h4>3.1 Adaptações na classe nativa principal</h4><p>Quando uma chamada é feita pelo Flutter, as seguintes condições precisam ser atendidas:</p><ol><li>O nativo deve ser capaz de tratar as chamadas;</li><li>O nativo deve ser capaz de receber chamadas.</li></ol><p>No Kotlin, para ser capaz de tratá-las, pode-se tornar a classe MainActivity uma implementação da classe MethodCallHandler, cuja função é justamente garantir que a classe que a implementa seja capaz de tratar chamadas oriundas de MethodChannel.</p><p>No Swift, não há necessidade de implementar um protocolo, basta criar um método dentro de AppDelegate. Isso será mostrado mais tarde neste tópico.</p><h4>3.2 Capacitando o nativo a receber chamadas</h4><p>Já para receber as chamadas, é necessário criar de fato um MethodChannel.</p><p>O construtor dele, em ambos nativos, recebe dois parâmetros:</p><ol><li>String que representa seu identificador único;</li><li>Uma instância de BinaryMessenger — conceito explorado posteriormente.</li></ol><h4>3.3 Conectando a recepção de chamadas com o tratamento</h4><p>No Kotlin, após a criação do MethodChannel, chama-se o método setMethodCallHandler passando um objeto que implementa a classe MethodCallHandler — neste caso, a própria MainActivity.</p><p>No Swift, diferente do Kotlin, o método setMethodCallHandler recebe um método, o qual fará o mesmo trabalho do método onMethodCall do Kotlin.</p><h4>3.4 Tratamento das chamadas</h4><p>Dada uma chamada x, existem alguns procedimentos básicos que podemos fazer, independente do nativo.</p><ol><li>Verifica-se se o nome do método invocado é válido; se existe um tratamento para ele do lado nativo.</li><li>Verifica-se se existe algum impedimento para a chamada ser bem-sucedida — caso contrário, entra-se em um caso de exceção.</li><li>Executa-se o tratamento adequado e responder de acordo com o esperado (sucesso ou erro).</li></ol><p>Vamos ver como esse conceito se aplica na prática.</p><h4>3.5 Consumindo a biblioteca TextToSpeech em Kotlin</h4><pre>package com.example.flutter_tts_app<br><br>import android.content.Context<br>import android.speech.tts.TextToSpeech<br>import io.flutter.embedding.android.FlutterActivity<br>import io.flutter.embedding.engine.FlutterEngine<br>import io.flutter.plugin.common.BinaryMessenger<br>import io.flutter.plugin.common.MethodCall<br>import io.flutter.plugin.common.MethodChannel<br>import java.util.*<br><br>class MainActivity : FlutterActivity(), MethodChannel.MethodCallHandler {<br><br>    private lateinit var textToSpeechChannel: MethodChannel<br>    private lateinit var textToSpeechService: TextToSpeech<br><br>    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {<br>        super.configureFlutterEngine(flutterEngine)<br>        initializeTextToSpeechChannel(flutterEngine.dartExecutor.binaryMessenger)<br>        initializeTextToSpeechService(applicationContext)<br>    }<br><br>    private fun initializeTextToSpeechChannel(binaryMessenger: BinaryMessenger) {<br>        textToSpeechChannel = MethodChannel(<br>            binaryMessenger, &quot;flutter_text_to_speech_channel&quot;<br>        )<br>        textToSpeechChannel.setMethodCallHandler(this)<br>    }<br><br>    private fun initializeTextToSpeechService(context: Context) {<br>        textToSpeechService = TextToSpeech(context) {<br>            if (it == TextToSpeech.SUCCESS) {<br>                textToSpeechService.language = Locale.forLanguageTag(&quot;BR&quot;)<br>            }<br>        }<br>    }<br><br>    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {<br>        if (call.method == &quot;speak&quot;) {<br>            val text = call.arguments&lt;String&gt;()<br>            if (text.isNullOrBlank()) {<br>                result.error(<br>                    &quot;INVALID_ARGUMENT&quot;,<br>                    &quot;O texto passado está vazio ou nulo.&quot;,<br>                    null // detalhes adicionais (pode ser um Map, por exemplo)<br>                )<br>                return<br>            }<br>            textToSpeechService.speak(text, TextToSpeech.QUEUE_FLUSH, null, null)<br>            result.success(null)<br>        } else {<br>            result.notImplemented()<br>        }<br>    }<br>}</pre><p>O código acima permite que o Flutter solicite que um texto digitado seja reproduzido em voz com uma biblioteca nativa do Kotlin.</p><p>Observe o código atentamente e note como fundamentos discutidos são postos em prática:</p><ul><li>MainActivity implementa MethodCallHandler, bem como o método onMethodCall (linha 12; linhas 38 a 54);</li><li>O MethodChannel é criado e recebe o responsável pelo tratamento de chamadas (linhas 23 a 28).</li><li>Quando as chamadas acontecem, elas são verificadas, processadas e devidamente respondidas por onMethodCall (linhas 38 a 54);</li><li>O método configureFlutterEngine, responsável por inicializar os serviços, é executado na inicialização do app (linhas 27 a 36).</li></ul><p>Agora que a estrutura fundamental das chamadas foi identificada, pode-se discutir como a solução foi desenvolvida:</p><ul><li>Após a inicialização do objeto TextToSpeech, configura-se o idioma de fala para português do Brasil (linhas 30 a 36);</li><li>Quando o método “speak” é invocado a partir do MethodChannel, captura-se e verifica-se se o texto enviado como argumento é nulo ou vazio. Caso seja, o nativo responde com erro. Caso contrário, chama-se o método speak do objeto TextToSpeech e passa-se o texto capturado como parâmetro (linhas 39 a 53).</li><li>Por fim, o nativo responde ao Flutter que a chamada foi bem-sucedida (linha 50).</li></ul><h4>3.6 Consumindo uma biblioteca nativa em Swift</h4><h4>3.6.1 Estrutura geral da classe AppDelegate</h4><p>No iOS, o ponto de entrada da aplicação é a classe AppDelegate. Quando o app é iniciado, o método application(_:didFinishLaunchingWithOptions:) é chamado. É nesse momento que se configura os canais de comunicação com o Flutter.</p><pre>@main<br>@objc class AppDelegate: FlutterAppDelegate {<br>  ...<br>}<br>// FlutterAppDelegate já cuida da integração <br>// básica do Flutter com o iOS, <br>// então é necessário apenas adicionar a lógica específica.</pre><h4>3.6.2 Inicialização do serviço de TTS</h4><pre>private var synthesizer: AVSpeechSynthesizer?<br><br>private func initializeTextToSpeechService() {<br>  synthesizer = AVSpeechSynthesizer()<br>}</pre><p>Aqui, usa-se o framework <strong>AVFoundation</strong> para criar um sintetizador de voz nativo. Ele é responsável por “falar” o texto recebido do Flutter.</p><h4>3.6.3 Criação do MethodChannel</h4><pre>private var textToSpeechChannel: FlutterMethodChannel?<br><br>private func initializeTextToSpeechChannel() {<br>  guard let controller = window?.rootViewController as? FlutterViewController else {<br>    print(&quot;Erro: não foi possível acessar o FlutterViewController&quot;)<br>    return<br>  }<br><br>  textToSpeechChannel = FlutterMethodChannel(<br>    name: &quot;flutter_text_to_speech_channel&quot;,<br>    binaryMessenger: controller.binaryMessenger<br>  )<br><br>  textToSpeechChannel?.setMethodCallHandler(handleMethodCall)<br>}</pre><p>Sobre o código acima, vale ressaltar:</p><ul><li>FlutterMethodChannel é a ponte entre oFlutter e o código nativo.</li><li>O <strong>nome do canal</strong> (flutter_text_to_speech_channel) deve ser o mesmo usado no lado Flutter.</li><li>setMethodCallHandler define um método (<em>callback</em>) que será chamadao sempre que o Flutter enviar uma mensagem.</li></ul><h4>3.6.4 Manipulando os métodos recebidos do Flutter</h4><pre>private func handleMethodCall(call: FlutterMethodCall, result: @escaping FlutterResult) {<br>  if call.method == &quot;speak&quot; {<br>    ...<br>  } else {<br>    result(FlutterMethodNotImplemented)<br>  }<br>}</pre><p>O método acima verifica o nome do método recebido. Se for &quot;speak&quot;, ele tenta extrair o texto e falar usando o sintetizador.</p><h4>3.6.5 Conversão de texto em fala</h4><pre>if let validText = text, isTextNotEmpty {<br>  let utterance = AVSpeechUtterance(string: validText)<br>  utterance.voice = AVSpeechSynthesisVoice(language: &quot;pt-BR&quot;)<br>  synthesizer?.speak(utterance)<br>  result(nil)<br>} else {<br>  result(FlutterError(code: &quot;INVALID_ARGUMENT&quot;, message: &quot;O texto passado está vazio ou nulo.&quot;, details: nil))<br>}</pre><p>No código acima:</p><ul><li>Cria-se um AVSpeechUtterance com o texto enviado pelo Flutter.</li><li>Define-se a voz para o idioma português do Brasil (pt-BR).</li><li>Usa-se o synthesizer para falar o texto.</li><li>É retornado sucesso (nil) ou erro com FlutterError.</li></ul><h4>3.6.6 AppDelegate.swift completo</h4><pre>import Flutter<br>import UIKit<br>import AVFoundation<br><br>@main<br>@objc class AppDelegate: FlutterAppDelegate {<br>  private var synthesizer: AVSpeechSynthesizer?<br>  private var textToSpeechChannel: FlutterMethodChannel?<br><br>  override func application(<br>    _ application: UIApplication,<br>    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?<br>  ) -&gt; Bool {<br>    GeneratedPluginRegistrant.register(with: self)<br><br>    initializeTextToSpeechService()<br>    initializeTextToSpeechChannel()<br><br>    return super.application(application, didFinishLaunchingWithOptions: launchOptions)<br>  }<br><br>  private func initializeTextToSpeechService() {<br>    synthesizer = AVSpeechSynthesizer()<br>  }<br><br>  private func initializeTextToSpeechChannel() {<br>    guard let controller = window?.rootViewController as? FlutterViewController else {<br>      print(&quot;Erro: não foi possível acessar o FlutterViewController&quot;)<br>      return<br>    }<br><br>    textToSpeechChannel = FlutterMethodChannel(<br>      name: &quot;flutter_text_to_speech_channel&quot;,<br>      binaryMessenger: controller.binaryMessenger<br>    )<br><br>    textToSpeechChannel?.setMethodCallHandler(handleMethodCall)<br>  }<br><br>  private func handleMethodCall(call: FlutterMethodCall, result: @escaping FlutterResult) {<br>    if call.method == &quot;speak&quot; {<br>      let text = call.arguments as? String<br>      let isTextNotEmpty = text?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty == false<br>      if let validText = text, isTextNotEmpty {<br>        let utterance = AVSpeechUtterance(string: validText)<br>        utterance.voice = AVSpeechSynthesisVoice(language: &quot;pt-BR&quot;)<br>        synthesizer?.speak(utterance)<br>        result(nil)<br>      } else {<br>        result(FlutterError(code: &quot;INVALID_ARGUMENT&quot;, message: &quot;O texto passado está vazio ou nulo.&quot;, details: nil))<br>      }<br>    } else {<br>      result(FlutterMethodNotImplemented)<br>    }<br>  }<br>}</pre><h4>💡 <strong>Conclusão</strong></h4><p>Dominar o Flutter vai além de saber usar widgets e consumir APIs em Dart: envolve também entender como ele <strong>se conecta às plataformas nativas</strong>.</p><p>Neste capítulo, mergulhamos nos <strong>fundamentos da comunicação entre Flutter e os mundos Kotlin e Swift</strong>. Exploramos como o Flutter faz chamadas usando MethodChannel, como o código nativo lida com essas chamadas, e o papel de cada lado nessa ponte.</p><p>Mesmo que o Flutter abstraia muita coisa, entender <strong>o que está acontecendo nos bastidores</strong> te dá mais confiança para lidar com integrações reais, e te prepara para desafios muito comuns em projetos profissionais.</p><p>🛠️ Esse conhecimento é o alicerce para criarmos, nos próximos capítulos, <strong>plugins Flutter mais robustos, bem estruturados e prontos para produção</strong>.</p><p>Se você chegou até aqui, parabéns. Agora você tem base suficiente para dar os próximos passos com muito mais segurança.</p><p>Nos vemos no próximo capítulo! 🙂</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=728cb5f49322" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Dev Flutter Sênior #1.1: A importância dos SDKs nativos na sua carreira]]></title>
            <link>https://adbysantos.medium.com/dev-flutter-s%C3%AAnior-1-1-a-import%C3%A2ncia-dos-sdks-nativos-na-sua-carreira-83239f6ca3bf?source=rss-36b5f6359189------2</link>
            <guid isPermaLink="false">https://medium.com/p/83239f6ca3bf</guid>
            <category><![CDATA[android]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[sdk]]></category>
            <dc:creator><![CDATA[Adby Santos]]></dc:creator>
            <pubDate>Mon, 28 Jul 2025 15:03:45 GMT</pubDate>
            <atom:updated>2025-07-28T15:03:45.315Z</atom:updated>
            <content:encoded><![CDATA[<p>Sim, você já deve estar cansado de saber. É fato mais do que comprovado o poder que o Flutter proporciona aos nossos apps: Interfaces de usuário encantadoras, performance comparável ao nativo e entregas mais rápidas.</p><p>Ao mesmo tempo, existe um tema até então <strong>pouco explorado </strong>(aqui fora, pelo menos).</p><p>Concorda comigo que um aplicativo consolidado, que gera receita, maduro, com anos de indústria, quase certamente <strong>não usa Flutter</strong>?</p><h3>Para o bem ou para o mal, Flutter <strong>é uma ferramenta nova</strong>.</h3><p>Antes do Flutter, e ainda hoje, apps utilizam tecnologias Android e iOS nativas, e, cá entre nós, elas estão <strong>longe de “morrer”</strong>, visto que são a porta de entrada para seus respectivos sistemas operacionais.</p><p>Além disso, assim como nós criamos e usamos pacotes, os desenvolvedores Android e iOS desses apps também fazem o mesmo. E mais: Investem muito tempo e dinheiro em testes, validações e certificações.</p><p>Nesse sentido, faz-se a seguinte reflexão: Um app consolidado, que gera receita, maduro e escalável desistiria de todo esse investimento para entrar no mundo Flutter e ter que <strong>fazer tudo do zero?</strong></p><p>Vamos lá: <strong>isso não faz sentido nenhum</strong>.</p><p>A realidade? Muitos apps consolidados adotam, sim, o Flutter. A questão não é se, mas <strong>como:</strong></p><ul><li>Como aproveitam o melhor dos dois mundos;</li><li>Como migram paulatinamente;</li><li>Como não jogam a água do banho com o bebê junto.</li></ul><p>Muitos desenvolvedores Flutter focam em bibliotecas puramente Dart, mas ignoram um ponto essencial para quem trabalha com apps: <strong>o ecossistema mobile ainda gira, em grande parte, em torno de SDKs nativos</strong>.</p><p>Gostaria de resolver isso, jovem gafanhoto? Bom, então pegue um café, acomode-se e… vem comigo!👇</p><figure><img alt="Imagem que apresenta uma representação visual de um SDK no contexto Flutter" src="https://cdn-images-1.medium.com/max/1024/1*bfQ5KY9wDbJdMNh14btWzQ.png" /><figcaption>Imagem: ChatGPT</figcaption></figure><h3>✅ O que é um SDK, afinal?</h3><p>Um <strong>SDK (Software Development Kit)</strong> é um conjunto de ferramentas, bibliotecas e recursos que permite que desenvolvedores integrem funcionalidades específicas nos seus aplicativos.<br>Ele pode incluir APIs, bibliotecas nativas, documentação, exemplos e às vezes até um painel de configuração.</p><p>Por exemplo:</p><ul><li>Um SDK bancário que oferece autenticação biométrica;</li><li>Um SDK de marketing que coleta eventos de <em>analytics</em>;</li><li>Um SDK de vídeo que lida com gravação e transmissão.</li></ul><h3>🧭 O Flutter ainda não é o centro do universo</h3><p>Embora o Flutter tenha crescido bastante, <strong>ele ainda é mais novo e menos dominante que Android (Java/Kotlin) e iOS (Swift/Obj-C)</strong>.</p><p>Isso significa que <strong>a maioria dos SDKs de mercado são feitos primeiro (ou exclusivamente) para Android e iOS</strong>.</p><p>Empresas que possuem SDKs nativos e querem oferecer suporte ao Flutter não costumam reescrever tudo em Dart. A abordagem mais comum e mais rápida é reaproveitar o SDK nativo existente. Nesse cenário, <strong>entra o papel do dev Flutter que entende do mundo nativo </strong>(vai por mim, não precisa entender muito).</p><h3>🤝 Como o Flutter conversa com esses SDKs?</h3><p>Para integrar SDKs nativos em um app Flutter, você tem basicamente duas abordagens:</p><ol><li><strong>Criar um plugin Flutter personalizado</strong> com:</li></ol><ul><li><strong>Kotlin/Java</strong> no Android (android/src/);</li><li><strong>Swift/Obj-C</strong> no iOS (ios/Classes/);</li><li>Usando <strong>Platform Channels</strong> para fazer a ponte entre o Dart e o código nativo.</li></ul><p><strong>2. Reutilizar um plugin existente (quando disponível)</strong>, mas mesmo assim entender a arquitetura envolvida para ajustar ou depurar.</p><p>É fato que boa parte das empresas, principalmente as consolidadas, caso venham a migrar para o Flutter, o farão cautelosamente, confiando essa tarefa à desenvolvedores habilidosos.</p><p>Se você ignora esse mundo, deixa na mesa muitas oportunidades valiosas de atuar, gerar resultado e se destacar.</p><h3>🧠 Por que entender SDKs é um diferencial?</h3><ul><li>Você <strong>consegue integrar qualquer serviço de terceiros</strong>, mesmo que ele só tenha SDKs nativos;</li><li>Você <strong>fala a língua das equipes iOS e Android</strong>, ganhando respeito técnico;</li><li>Você evita o famoso “bloqueio Flutter”: “só posso usar se tiver plugin pronto”;</li><li>Você pode <strong>criar seus próprios plugins</strong> e contribuir com a comunidade.</li></ul><p>Em empresas maiores (e até mesmo em startups que usam SDKs de parceiros), isso <strong>te posiciona como um dev sênior que resolve problemas;</strong> não apenas que “usa pacotes”, mas que planeja, desenvolve e aprimora soluções.</p><h3>💡 Conclusão</h3><p>O Flutter é uma ponte entre mundos e, para dominá-lo de verdade, você precisa entender o que existe dos dois lados.</p><p>Saber o que é um SDK e como reutilizá-lo no seu app Flutter é um passo importante para se tornar um desenvolvedor mais completo, confiante e valorizado.</p><p>Se você está estudando para subir de nível no Flutter, meu conselho é simples: <strong>dê uma olhada nos SDKs nativos e aprenda como conectá-los ao Dart</strong>. É aí que muitos se destacam.</p><p>Se você está estudando para subir de nível no Flutter, o conselho é simples:</p><ul><li>Leia a documentação do Flutter sobre <a href="https://docs.flutter.dev/packages-and-plugins/developing-packages#plugin">integração com SDKs nativos</a>.</li><li><strong>Ponha a mão na massa: Escolha um SDK nativo, crie um plugin e conecte-o a um app Flutter</strong>.</li></ul><p>Na parte 2 desta tema, vamos desenvolver um plugin Flutter do zero, desde sua concepção e arquitetura, até a integração com um app.</p><p>Até a próxima. :)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*DO13t0nJmuHvyBFd" /><figcaption>Photo by <a href="https://unsplash.com/@bearsnap?utm_source=medium&amp;utm_medium=referral">Junseong Lee</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=83239f6ca3bf" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Breves considerações sobre Tratamento de Exceções no Flutter]]></title>
            <link>https://medium.com/flutterando/breves-considera%C3%A7%C3%B5es-sobre-tratamento-de-exce%C3%A7%C3%B5es-no-flutter-d3e579dea14f?source=rss-36b5f6359189------2</link>
            <guid isPermaLink="false">https://medium.com/p/d3e579dea14f</guid>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[flutter-web]]></category>
            <category><![CDATA[flutter-app-development]]></category>
            <category><![CDATA[error]]></category>
            <category><![CDATA[error-handling]]></category>
            <dc:creator><![CDATA[Adby Santos]]></dc:creator>
            <pubDate>Thu, 06 Apr 2023 20:54:55 GMT</pubDate>
            <atom:updated>2023-04-06T21:07:55.520Z</atom:updated>
            <content:encoded><![CDATA[<p>Saber isso pode fazer a diferença na sua carreira.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*EGjRIfWwxcOi0S2g" /><figcaption>Photo by <a href="https://unsplash.com/@roketpik?utm_source=medium&amp;utm_medium=referral">Artur Shamsutdinov</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h4>1. Exceções são comuns, acostume-se</h4><p>Ao analisarmos qualquer código, a primeira coisa que devemos nos perguntar é: o que pode dar errado nele?</p><p>Tomemos como exemplo o que pode dar errado ao realizar login em uma aplicação:</p><ul><li>A internet pode cair;</li><li>O servidor pode cair;</li><li>O e-mail pode ser inválido;</li><li>A senha pode ser incorreta.</li></ul><p>Enfim, acesso ao GPS, Bluetooth, Câmera… tudo isso pode sofrer com exceções. O usuário pode negar permissão ao GPS, ou ao Bluetooth; a câmera pode parar de funcionar. Logo, tratar exceções é algo muito natural.</p><h4>2. Como saber qual exceção ocorreu?</h4><p>Vamos tomar como exemplo uma requisição a uma API Restful.</p><p>Geralmente, quando uma exceção ocorre, o client Http que você está usando joga (<em>throws</em>) uma exceção em forma de objeto, o qual contém informações sobre o que aconteceu, como <em>status code</em> e <em>message</em>.</p><p>O ideal é que, se, por exemplo, na operação de login, a senha for incorreta, a forma como a API define o que aconteceu seja sempre a mesma (<em>e.g.</em> <em>message</em> == &#39;senha_incorreta’). Em geral, a resposta para a pergunta “qual exceção ocorreu&quot; está (ou deveria estar) dentro do objeto que representa a exceção.</p><p>Nesse caso, já que falamos de Client Http, se você utiliza o pacote <a href="https://pub.dev/packages/dio">Dio</a>, a exceção é o <a href="https://pub.dev/documentation/dio/latest/dio/DioError-class.html">DioError</a>; já se você utiliza o <a href="https://pub.dev/packages/uno">Uno</a>, é o <a href="https://pub.dev/documentation/uno/latest/uno/UnoError-class.html">UnoError</a>, e assim por diante.</p><h4>3. Como dizer o que aconteceu ao usuário?</h4><p>A dica é sempre apresentar mensagens úteis, objetivas, que indiquem ao usuário exatamente o que aconteceu e, se for o caso, o que ele pode fazer para corrigir.</p><p>Desse modo, alcança-se um tratamento de exceções que não apenas agrada aos desenvolvedores, mas também, e principalmente, aos usuários.</p><h4>4. Considerações finais</h4><p>É válido salientar: Não adianta somente saber qual exceção ocorreu, é preciso explicar adequadamente ao usuário, afinal, ele é o centro de todo o processo de desenvolvimento.</p><p>Também não adianta um app feito apenas de mensagens bem feitas, é preciso ter regras de negócio bem construídas ou em evolução, bem como recursos (<em>e.g.</em> APIs e pacotes) adequados para bem servir ao seu projeto.</p><p>Quer saber mais sobre Flutter e acelerar seus estudos?<br>Me siga para sempre estar por dentro das novidades 🚀</p><h3>🌐 Minhas redes sociais 🌐</h3><p><a href="https://www.github.com/blackleg15">GitHub</a> | <a href="https://www.linkedin.com/in/adby-santos-a31a70158/">LinkedIn</a> | <a href="https://www.instagram.com/oadbysantos/">Instagram </a>| <a href="https://youtu.be/IhNoWGYREhw">YouTube</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d3e579dea14f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/flutterando/breves-considera%C3%A7%C3%B5es-sobre-tratamento-de-exce%C3%A7%C3%B5es-no-flutter-d3e579dea14f">Breves considerações sobre Tratamento de Exceções no Flutter</a> was originally published in <a href="https://medium.com/flutterando">Flutterando</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[O motivo de você não aprender Flutter — e como corrigí-lo em 3 passos]]></title>
            <link>https://adbysantos.medium.com/o-motivo-de-voc%C3%AA-n%C3%A3o-aprender-flutter-e-como-corrig%C3%AD-lo-em-3-passos-a39fc201e9e2?source=rss-36b5f6359189------2</link>
            <guid isPermaLink="false">https://medium.com/p/a39fc201e9e2</guid>
            <category><![CDATA[apps]]></category>
            <category><![CDATA[iniciante]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[desenvolvimento]]></category>
            <dc:creator><![CDATA[Adby Santos]]></dc:creator>
            <pubDate>Mon, 02 Jan 2023 14:52:03 GMT</pubDate>
            <atom:updated>2023-01-07T01:24:20.717Z</atom:updated>
            <content:encoded><![CDATA[<p>Flutter está a todo vapor em 2023. Muitos devs, porém, relatam dificuldade para compreendê-lo. Você tem essa dificuldade? Quer saber como resolver isso? É sobre isso que falaremos hoje.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*2t4Pp5gVibbTRcmT" /><figcaption>Photo by <a href="https://unsplash.com/@bruno_nascimento?utm_source=medium&amp;utm_medium=referral">Bruno Nascimento</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3>Se quer aprender, saia da escola</h3><p>Uma coisa é fato: Praticamente todos nós, brasileiros, fomos influenciados direta ou indiretamente pelo sistema educacional do país.</p><p>Segundo o último ranking do <a href="https://factsmaps.com/pisa-2018-worldwide-ranking-average-score-of-mathematics-science-reading/">PISA,</a> estamos em 66° lugar de 77 países, ou seja, estamos nos 15% piores países na educação.</p><p>Vejamos a forma como o estudante médio estuda: assiste à aula; usa celular; dorme. De novo e de novo. Na véspera de avaliação, estuda todos os assuntos com a “esperança” de que isso resolva. E o pior: às vezes resolve. Porém, ele esquece simplesmente TUDO logo após. No livro <a href="https://www.amazon.com.br/Aprendendo-intelig%C3%AAncia-instru%C3%A7%C3%B5es-c%C3%A9rebro-estudantes/dp/8576572052/ref=asc_df_8576572052/?tag=googleshopp06-20&amp;linkCode=df0&amp;hvadid=379736221162&amp;hvpos=&amp;hvnetw=g&amp;hvrand=14391495038707995021&amp;hvpone=&amp;hvptwo=&amp;hvqmt=&amp;hvdev=m&amp;hvdvcmdl=&amp;hvlocint=&amp;hvlocphy=20103&amp;hvtargid=pla-590242505735&amp;psc=1">Aprendendo Inteligência</a>, do professor Pierluigi Piazzi, ele dá o exemplo do fisiculturista (popularmente, “o cara que vai para a academia malhar&quot;): seu treinador te dá mil horas para ter um físico decente dentro de três anos. O que o brasileiro médio faria? Deixaria pra lá por dois anos e, nos últimos cem dias, treinaria dez horas por dia. Precisamos comentar o resultado? ¯⁠\⁠_⁠(⁠ツ⁠)⁠_⁠/⁠¯</p><p>Nosso sistema educacional é muito ruim. Não há incentivo à qualidade, e sim à quantidade de alunos e ao rendimento temporário destes — e que se exploda o longo prazo.</p><p>Resultado: temos pessoas que não sabem estudar, e quem não sabe estudar, não aprende, logo não gosta de estudar, e, já que não vê resultado, logo não investirá tempo nisso, pois é desprazeroso, cansativo e não dá retorno. É o famoso “estudar pra quê se eu não aprendo?”.</p><h3>O aprendizado começa onde a escola termina</h3><p>Segundo o professor Piazzi, a verdade é que todo curso, escola, colégio, faculdade… TUDO. TUDO ISSO serve apenas para UMA COISA: te motivar a estudar sozinho.</p><blockquote>“O bom professor não dá aula para fazer o aluno <strong>aprender</strong>. Ele dá aula para fazer o aluno <strong>entender</strong> a matéria e, principalmente, fazê-lo <strong>gostar</strong> do que está sendo apresentado.”</blockquote><p>Isso vira o jogo: agora fica claro que o estudo autodidata é a chave do entendimento. Em outras palavras, <strong>para aprender Flutter de verdade, você precisa estudar sozinho</strong> <strong>e consultar os mais experientes</strong>.</p><h3><strong>Mas como posso estudar? Como dominar a técnica dos estudos?</strong></h3><p>Caso queira um manual completo de como estudar, recomendo muito o livro já mencionado <a href="https://www.amazon.com.br/Aprendendo-intelig%C3%AAncia-instru%C3%A7%C3%B5es-c%C3%A9rebro-estudantes/dp/8576572052/ref=asc_df_8576572052/?tag=googleshopp06-20&amp;linkCode=df0&amp;hvadid=379736221162&amp;hvpos=&amp;hvnetw=g&amp;hvrand=14391495038707995021&amp;hvpone=&amp;hvptwo=&amp;hvqmt=&amp;hvdev=m&amp;hvdvcmdl=&amp;hvlocint=&amp;hvlocphy=20103&amp;hvtargid=pla-590242505735&amp;psc=1">Aprendendo Inteligência</a>, do professor Piazzi.</p><p>Para aprender Flutter, especificamente, eu fiz um vídeo exatamente sobre isso. <a href="https://www.youtube.com/watch?v=IhNoWGYREhw&amp;list=PLuNXifGM1F3sJRDZZmiCRzI-pB6DLkDsU">Assista aqui</a>.</p><p>As dicas apresentadas no vídeo são derivadas da minha experiência, tanto como desenvolvedor quanto como mentor, do que pode funcionar. Lembre-se disso: As formas de aprender são inúmeras, então escolha um número limitado delas (três, por exemplo) e aplique. Pode ser que algo que funcione para mim, não funcione para você, e vice-versa, porém, e isso serve para todo mundo, para aprender, você precisa estudar sozinho.</p><h3>Recomendações finais (ou resumo para os mais apressados)</h3><p>Leia o livro “<a href="https://www.amazon.com.br/Aprendendo-intelig%C3%AAncia-instru%C3%A7%C3%B5es-c%C3%A9rebro-estudantes/dp/8576572052/ref=asc_df_8576572052/?tag=googleshopp06-20&amp;linkCode=df0&amp;hvadid=379736221162&amp;hvpos=&amp;hvnetw=g&amp;hvrand=14391495038707995021&amp;hvpone=&amp;hvptwo=&amp;hvqmt=&amp;hvdev=m&amp;hvdvcmdl=&amp;hvlocint=&amp;hvlocphy=20103&amp;hvtargid=pla-590242505735&amp;psc=1">Aprendendo Inteligência</a>&quot;;</p><p>Assista ao vídeo <a href="https://youtube.com/playlist?list=PLuNXifGM1F3sJRDZZmiCRzI-pB6DLkDsU">“Como aprender Flutter mais rápido&quot;</a>;</p><p>E faça tudo isso <strong>agora, ou agende para hoje mais tarde</strong></p><p>Quer saber mais sobre Flutter e acelerar seus estudos?<br>Me siga para sempre estar por dentro das novidades 🚀</p><h3>🌐 Minhas redes sociais 🌐</h3><p><a href="https://www.github.com/blackleg15">GitHub</a> | <a href="https://www.linkedin.com/in/adby-santos-a31a70158/">LinkedIn</a> | <a href="https://www.instagram.com/oadbysantos/">Instagram </a>| <a href="https://youtu.be/IhNoWGYREhw">YouTube</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a39fc201e9e2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Clean Dart é viável em 2023? — parte 2]]></title>
            <link>https://medium.com/flutterando/clean-dart-%C3%A9-vi%C3%A1vel-em-2023-parte-2-b39f93731247?source=rss-36b5f6359189------2</link>
            <guid isPermaLink="false">https://medium.com/p/b39f93731247</guid>
            <category><![CDATA[arquitetura-limpa]]></category>
            <category><![CDATA[flutterando]]></category>
            <category><![CDATA[clean-dart]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[clean-architecture]]></category>
            <dc:creator><![CDATA[Adby Santos]]></dc:creator>
            <pubDate>Thu, 15 Dec 2022 14:10:03 GMT</pubDate>
            <atom:updated>2022-12-15T14:10:03.456Z</atom:updated>
            <content:encoded><![CDATA[<h3>Clean Dart é viável em 2023? — parte 2</h3><p>“Não é o mais forte o que sobrevive, nem o mais inteligente, mas o que melhor se adapta às mudanças.” Leon C. Megginson.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*DoMFstiWVV7KWnpv.png" /><figcaption>A comunicação entre camadas no Clean Dart</figcaption></figure><p>Viabilidade significa estar adequado a algo. Nesse sentido, para o Clean Dart estar adequado às diferentes necessidades das equipes e projetos, precisamos realizar nele adaptações (conscientes e embasadas).</p><p>Nesta parte 2, discutiremos sobre uma característica bastante comentada a respeito do Clean Dart: <strong>a verbosidade</strong>.</p><h3>1 A verbosidade do Clean Dart</h3><h4>1.1 Clean Code</h4><p><strong>O Clean Dart tradicional</strong>, visto na parte 1, implica na construção de arquivos e classes decorados com ou o nome do caso de uso ou da página a qual estão relacionados. Essa prática deriva de sugestões muito comuns na área de programação, semelhantes àquelas encontradas no livro Clean Code (2009), onde o autor sugere que <strong>as classes e arquivos dos projetos devem ser claros em suas funções</strong>.</p><h4>1.2 O possível problema</h4><p>Porém, quais outros resultados derivam dessa prática?</p><p>É possível observar um <strong>considerável grau de verbosidade</strong> durante a constituição da arquitetura. <strong>O quanto isso incomoda?</strong> Bem, o incômodo é totalmente relativo à equipe envolvida.</p><h4>1.3 Como solucionar?</h4><p>Como quase tudo na programação, existem <em>n</em> soluções. Dentre elas, vamos citar duas:</p><ol><li>Adaptação</li><li>Automatização.</li></ol><h3>2 Adaptando o Clean Dart</h3><p>Adaptação é o movimento de mudança para resolver um problema específico. Nesse sentido, <strong>poder-se-ia adaptar o Clean Dart de forma que ele contemplasse um grau de verbosidade menor do que o usual</strong>.</p><p>Como faremos isso?</p><p>Vamos lá.</p><p>2.1 Aproveitando-se da repetitividade</p><p>É possível notar que o Clean Dart é <strong>bastante repetitivo</strong>. Assinaturas de Usecases e Pages são os itens mais repetidos. Nesse sentido, esta abordagem pretende reduzir a verbosidade através de uma <strong>reorganização das camadas</strong>.</p><p><strong>Como assim?</strong></p><p>Sustentada na ideia de que casos de uso e páginas são os itens orientantes dos arquivos e classes, esta abordagem define um novo escopo para os arquivos.</p><p><strong>Vamos a um exemplo.</strong></p><p>Vejamos a camada Domain desta abordagem. Ela não mais contempla vários diretórios (<em>usecases</em>, <em>entities</em>, <em>errors </em>etc), mas sim apenas o diretório <em>usecases</em>.</p><p>Dentro de<em> usecases</em> teremos acesso a cada usecase do projeto, como antes. Porém, dentro do diretório de cada usecase, encontraremos os diretórios anteriormente encontrados na camada Domain.</p><p>Não apenas isso: <strong>os arquivos e classes dentro do diretório de cada usecase não mais terão a assinatura do caso de uso</strong>.</p><p>Por que? Porque o escopo define isso.</p><p>Resumindo: <strong>Temos uma arquitetura fortemente baseada em seu escopo.</strong></p><p>Que benefícios podemos observar?</p><ol><li><strong>Reaproveitamento de código:</strong> Podemos rapidamente criar módulos aproveitando-nos do código de outros já construídos.</li><li><strong>Tempo ganho:</strong> Os arquivos e classes agora custam menos na criação e manutenção.</li></ol><p>Porém, quais desvantagens encontramos?</p><ol><li><strong>Menos clareza:</strong> É nítido que o nível de clareza da arquitetura está relativamente menor do que na abordagem tradicional.</li><li><strong>Ausência de singularidade: </strong>Devido a repetitividade na assinatura dos arquivos, práticas como buscar arquivos pelo nome e importação são relativamente mais difíceis nesta abordagem.</li></ol><p>Vejamos <a href="https://github.com/BlackLeg15/learning_architecture/tree/master/clean_dart">aqui</a> um exemplo de projeto que aplica um Clean Dart menos verboso (digamos, um Clean Dart <em>light</em>).</p><h3>3 Automatizando o Clean Dart</h3><p>Automatização é sinônimo de pular etapas. Mas de qual etapa estamos falando?</p><p><strong>Da etapa de construção do módulo.</strong></p><p>A primeira solução teve como princípio a existência de padrões no Clean Dart. Todo padrão traz consigo certo grau de repetitividade, concorda? No passo da escrita da arquitetura, precisamos várias vezes digitar os mesmos caracteres. <strong>É exatamente aqui que a automatização toma parte</strong>.</p><p>A solução que sugiro tem como base a geração de código através de uma Command-line Interface (CLI).</p><p>Essa CLI construiria a estrutura-padrão do Clean Dart. Isso resolveria rapidamente o problema da verbosidade durante a construção do módulo.</p><p><strong>CLIs com Clean Dart existem?</strong></p><p>Sim. Vamos a algumas.</p><h4>3.1 Slidy</h4><p>O Slidy é uma CLI desenvolvida pela comunidade Flutterando. Um de seus principais mantenedores é o Jacob Moura, GDE de Flutter no Brasil.</p><p>Tal CLI é capaz de não só gerar códigos do Clean Dart, mas também widgets, repositories e até uma arquitetura <em>feature-based</em>.</p><p>Sugiro a todos conferirem o Slidy. Deixo esta dica: comentem nas redes da Flutterando a respeito do Slidy. Quanto mais comentários, mais suporte será dado a essa CLI (em outras palavras, encham o saco do Jacob).</p><p>Vamos para o próximo.</p><h4><strong>3.2 Mason</strong></h4><p>O Mason é uma espécie de CLI, porém eu gostaria de definí-lo como um gerador de códigos.</p><p>Esse gerador é capaz de ler um conjunto de instruções (chamados <em>bricks</em>) e gerar código. O objetivo dele é resolver tarefas repetitivas e permitir que os desenvolvedores foquem nas especificidades das aplicações.</p><p><strong>3.2.1 Como o Clean Dart se encaixa no Mason?</strong></p><p>O <em>brick</em> que o Mason lê não é dele: é de todos aqueles que decidirem utilizá-lo. Isso que dizer que tanto eu quanto você podemos construir <em>bricks</em>.</p><p>Dessa forma, podemos construir um <em>brick</em> para gerar a estrutura do Clean Dart.</p><p><strong>3.2.2 Um<em> brick</em> do Clean Dart já existe?</strong></p><p>Até a data deste artigo, não encontrei. A propósito, os <em>bricks</em> estão disponíveis no <a href="https://brickhub.dev/">BrickHub</a>.</p><p>No futuro, vocês podem contribuir com <em>bricks</em> já existentes, <em>forkar</em> outros <em>bricks</em> ou ainda criar os seus próprios<em> bricks</em>.</p><p>Quem sabe não teremos uma parte 3 desta série construindo um <em>brick </em>chamado <em>clean_dart</em>? 👀</p><p>Vocês gostariam? Deixem aí nos comentários.</p><h3>4 Clean Dart em 2023</h3><p>Clean Dart, assim como arquitetura, é um assunto que atrai vários interesses, tais como produtividade, sucesso e escala.</p><p>Para alcançarmos isso, a melhor habilidade que podemos exercer é <strong>pensar no longo prazo</strong>.</p><p>Todas as linhas de código são justificadas pelo longo prazo; pelo benefício que elas oferecem. <strong>Pouco importa se você aprendeu uma arquitetura nova. </strong>Os problemas que queremos resolver <strong>sempre</strong> se sobressairão sobre todo e qualquer aspecto da programação.</p><p>“Clean Dart é viável em 2023?”</p><p>Essa <strong>não</strong> é a pergunta certa.</p><p>A pergunta é: <strong>você está pronto para 2023</strong>?</p><p><strong>As soluções só se adaptam quando nós nos adaptamos aos problemas.</strong></p><p>Nossos problemas não são resolvidos pela programação. Eles são resolvidos por nós. A programação, assim como o Clean Dart, é apenas mais um meio de resolvermos esses problemas.</p><p>Adapte-se ou pereça. <br>A escolha é sua. 👊</p><p>Quer saber mais sobre Clean Dart e Flutter?<br><strong>Me siga para sempre estar por dentro das novidades 🚀</strong></p><p>(O seu clap me ajuda bastante 👏)</p><h3>🌐 Minhas redes sociais 🌐</h3><p><a href="https://github.com/BlackLeg15">GitHub</a> | <a href="https://www.linkedin.com/in/adby-santos-a31a70158/">LinkedIn </a>| <a href="https://www.instagram.com/oadbysantos/">Instagram</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b39f93731247" width="1" height="1" alt=""><hr><p><a href="https://medium.com/flutterando/clean-dart-%C3%A9-vi%C3%A1vel-em-2023-parte-2-b39f93731247">Clean Dart é viável em 2023? — parte 2</a> was originally published in <a href="https://medium.com/flutterando">Flutterando</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Clean Dart é viável em 2022? — parte 1]]></title>
            <link>https://medium.com/flutterando/clean-dart-%C3%A9-vi%C3%A1vel-em-2022-parte-1-fcc76de85624?source=rss-36b5f6359189------2</link>
            <guid isPermaLink="false">https://medium.com/p/fcc76de85624</guid>
            <category><![CDATA[clean-architecture]]></category>
            <category><![CDATA[clean-dart]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[arquitetura-limpa]]></category>
            <dc:creator><![CDATA[Adby Santos]]></dc:creator>
            <pubDate>Mon, 12 Dec 2022 16:57:37 GMT</pubDate>
            <atom:updated>2022-12-22T13:01:27.829Z</atom:updated>
            <content:encoded><![CDATA[<h3>Clean Dart é viável em 2023? — parte 1</h3><p>Toda boa arquitetura evolui; isso é fato. Assim, esta série de artigos é destinada a contribuir com a evolução do Clean Dart.</p><p>Mais especificamente, esta série visa:</p><ol><li>Expor e justificar a forma como eu costumo utilizar o Clean Dart em projetos Flutter;</li><li>Contribuir com a evolução do Clean Dart;</li><li>Evoluir a partir de eventuais críticas.</li></ol><p>Em outras palavras, a parte 1 tem, como objetivo, expor a forma como eu particularmente descrevo <strong>o</strong> <strong>Clean Dart tradicional</strong>.</p><p>Sem mais delongas, vamos começar.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/585/1*0FFQkhcyEXds7tGbOcKgQQ.png" /></figure><h3>1 Introdução</h3><h4>1.1 O que é Clean Dart?</h4><p>Clean Dart é uma abordagem de arquitetura limpa para a linguagem de programação Dart.</p><h4>1.2 O que é Dart?</h4><p>Dart é uma linguagem de programação a qual, entre várias utilidades, é a principal linguagem de programação do Flutter.</p><h4>1.3 O que é Flutter?</h4><p>Flutter é um UI toolkit da Google para construção de experiências multiplataforma a partir de um único código-fonte.</p><p>Um exemplo disso? Um app para Android/iOS.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iIszAmbftffUx_6FQ8bXNQ.png" /><figcaption>Camadas do Clean Dart</figcaption></figure><h3>2 Vamos falar de Clean Dart</h3><h4>2.1 Práticas</h4><p>A proposta do Clean Dart sugere as seguintes práticas:</p><p>Divisão de código <em>feature-based (module-based)</em>;<br>Aplicação da arquitetura por feature/módulo (não no app como um todo);</p><h4>2.2 Camadas</h4><p><strong>2.2.1 Presenter</strong></p><p>A camada Presenter é responsável pela disposição dos recursos visuais de um módulo.</p><p>Obs.: <em>Strictu sensu</em>, Pages são Widgets. Nesta seção, leve em consideração a diferença no uso de cada termo.</p><p>2.2.1.1 Pages</p><p>Widgets que representam telas cheias.</p><p>2.2.1.2 Widgets</p><p>Widgets que representam componentes (não telas cheias).</p><p>2.2.1.3 Controllers</p><p>Classes que concentram funções utilizadas por Pages ou Widgets.</p><p>2.2.1.4 Stores</p><p>Classe responsável pela gerência de estado de um caso de uso, ou seja, de manter o estado atual do caso de uso, bem como as funções responsáveis por alterá-lo.</p><p>2.2.1.4 States</p><p>Classe responsável por definir os possíveis estados de um caso de uso, os quais são geridos pela classe Store e consumidos pelas Pages.</p><p><strong>2.2.2 Domain</strong><br>A camada Domain é responsável pela definição dos dados específicos utilizados em seu módulo.</p><p>2.2.2.1 Repository (classe abstrata)</p><p>Classe responsável por definir como os dados serão recuperados da camada Infra.</p><p>2.2.2.2 Service</p><p>Classe responsável por definir como um serviço será fornecido pela camada Infra para um ou mais casos de uso.</p><p>2.2.2.3 Errors</p><p>Classe responsável por definir quais erros podem acontecer para um determinado caso de uso.</p><p>2.2.2.4 Parameters</p><p>Classe responsável por definir quais parâmetros são recebidos por um determinado caso de uso.</p><p>2.2.2.5 Usecase (classe abstrata)</p><p>Classe responsável por definir a forma como um caso de uso é consumido naquele módulo.</p><p>2.2.2.6 Usecase (implementação)</p><p>Classe responsável por verificar os parâmetros do caso de uso e solicitar informações a repositórios ou serviços.</p><p>2.2.2.7 Typedef</p><p>Arquivo responsável por definir os tipos de retorno dos métodos dos casos de uso, assim como de seus respectivos repositórios.</p><p><strong>2.2.3 Camada Infra</strong></p><p>A camada Infra é responsável pelos recursos-base do módulo.</p><p>2.2.3.1 Repository (implementação)</p><p>Classe responsável por realizar o tratamento de exceções geral da utilização dos recursos externos.</p><p>2.2.3.2 Service (implementação)</p><p>Classe responsável por realizar o tratamento de exceções geral da utilização dos recursos do dispositivo (plataforma) do app.</p><p>2.2.3.3 Datasource (classe abstrata)</p><p>Classe responsável por definir a forma como um datasource é consumido pelo repository.</p><p>2.2.3.4 Driver (classe abstrata)</p><p>Classe responsável por definir o mínimo necessário que nosso caso de uso necessita</p><p><strong>2.2.4 Camada External</strong></p><p>A camada External é responsável pela comunicação com recursos externos (i.e. APIs etc).</p><p>2.2.4.1 Datasource</p><p>Classe responsável pela comunicação entre o aplicativo e algum recurso externo, por exemplo, uma API.</p><p>2.2.4.2 Mapper</p><p>Classe responsável pela tradução das respostas dos recursos externos para Dart; mais especificamente, para uma entidade (ou entidades) esperadas pelo caso de uso.</p><p>2.2.4.3 Driver</p><p>Classe responsável pela comunicação entre o aplicativo e algum recurso ou funcionalidade da plataforma do aplicativo, e.g., acelerômetro, câmera etc.</p><h4>2.3 Nome dos arquivos</h4><p>Obs. 1: Todos os arquivos são finalizados com “.dart”.</p><p>Obs. 2: Classes implementações de contratos são decorados com “_impl”.</p><p><strong>2.3.1 Camadas Domain, Infra e External</strong></p><p>Nestas camadas, podemos observar que os arquivos estão em função dos casos de uso. Dito isso, vejamos alguns exemplos:</p><p>Fórmula: caso_de_uso + _função_do_arquivo</p><p>Exemplo 1: get_all_students_usecase.dart</p><p>Exemplo 2: get_all_students_usecase_impl.dart</p><p><strong>2.3.2 Camada Presenter</strong></p><p>2.3.2.1 Controllers, pages e widgets</p><p>Sugestão: identificação + _função_do_arquivo</p><p>Exemplo: home_page.dart</p><p>2.3.2.2 Stores</p><p>Stores são orientadas aos casos de uso, ou seja, seguem a linha apresentada na seção 2.3.1.</p><p>Fórmula: caso_de_uso + _store</p><p>Exemplo: get_all_students_store.dart</p><h3>3. Insights</h3><h4><strong>3.1 Clean Dart é um modelo estático?</strong></h4><p>O Clean Dart tradicional é uma estrutura dividida em quatro camadas. Sugere-se a utilização dele em um contexto de módulos independentes. Para mais informações, vejam os apps-exemplos na <a href="https://github.com/Flutterando/Clean-Dart">página dele</a>.</p><p>Porém, é mais do que óbvio que:</p><ol><li>Apps podem ter necessidades diferentes;</li><li>Logo, apps podem não utilizar a mesma arquitetura.</li></ol><p><strong>Como proceder?</strong></p><p>É possível adaptar o Clean Dart para atender a essas necessidades. Para tal, cabe aos especialistas envolvidos discernirem o melhor modelo a seguir.</p><p>É útil para isso observarmos, sempre, o objetivo final; o por que; o motivo de fazermos o que fazemos. Definir bem os porquês é pré-requisito para definir bem os princípios e regras de toda arquitetura.</p><p><strong>3.1.1 Uma entidade em mais de um caso de uso</strong></p><p>Existem projetos cujo backend tem prioridade sobre o frontend. Nesses casos, é possível observar reincidência de utilização de uma mesma entidade em casos de uso diferentes.</p><p><strong>Como proceder?</strong></p><p>Defino uma entidade independente do caso de uso, assim mais de um caso de uso pode fazer uso dessa entidade.</p><h4><strong>3.2 Orientação das camadas</strong></h4><p><strong>3.2.1 Presenter</strong></p><p>Tanto na teoria quanto na prática, é possível visualizar que, na camada Presenter, as páginas têm prioridade. Por que? Porque páginas podem contemplar mais de um caso de uso.</p><p><strong>3.2.2 Demais camadas (Domain, Infra, External)</strong></p><p>A partir do exposto, é possível concluir que as demais camadas são orientadas pelos casos de uso. Por que? Porque elas estão diretamente ligadas ao caso de uso. Como assim? Veja que se o caso de uso for utilizado em páginas diferentes, toda sua estrutura (repository, datasource etc) também será utilizada.</p><p><strong>3.2.3 A relação entre Store e Page</strong></p><p><strong>Não existe uma dependência direta entre páginas e casos de uso.</strong> Ambos lados são totalmente independentes, ou seja, as páginas podem ter os casos de uso que quiserem e os casos de uso podem ser utilizados em quaisquer páginas.</p><p><strong>Quando essa dependência passa a existir?</strong></p><p>Com a introdução da Store. Por que? Porque defino a gerência de estado na Store.</p><p>A partir daqui, por motivos de custo-benefício, faço a Page utilizar o mecanismo fornecido pela gerência de estado (e.g. BLoC) para atualizar os componentes interessados no estado do caso de uso da Store.</p><p>Uma nota sobre gerência de estado:</p><p>Por fins didáticos, chamo “MobX” e “BLoC” seus respectivos pacotes mais famosos utilizados, i.e., flutter_bloc e flutter_mobx. Atenção: <strong>Não confundam um pacote com um padrão. São coisas diferentes.</strong></p><h3>4 O que está além do tradicional?</h3><p>Chegamos em um campo experimental. A partir da parte 2, veremos algumas abordagens adaptadas para atender necessidades usuais. <strong>O principal objetivo é expô-las a críticas e evoluir</strong>.</p><p>Até o momento, posso adiantar para vocês que:<br>A parte 2 trará <strong>uma</strong> <strong>abordagem menos verbosa</strong>;<br>A parte 3 trará <strong>uma abordagem que separa casos de uso e páginas</strong>.</p><p>Como será feito?<br>Justificativas?</p><p>Continua…</p><p>Tem interesse em Clean Dart e Flutter?<br><strong>Siga-me aqui e nas </strong><a href="https://www.instagram.com/oadbysantos/"><strong>minhas redes sociais</strong></a><strong>.</strong><br>Através delas, vocês saberão <strong>primeiro</strong> os novos temas de artigos.</p><h3>🌐 Minhas redes sociais 🌐</h3><p><a href="https://github.com/BlackLeg15">GitHub</a> | <a href="https://www.linkedin.com/in/adby-santos-a31a70158/">LinkedIn </a>| <a href="https://www.instagram.com/oadbysantos/">Instagram</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fcc76de85624" width="1" height="1" alt=""><hr><p><a href="https://medium.com/flutterando/clean-dart-%C3%A9-vi%C3%A1vel-em-2022-parte-1-fcc76de85624">Clean Dart é viável em 2022? — parte 1</a> was originally published in <a href="https://medium.com/flutterando">Flutterando</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>