<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Filipe Nanclarez</title>
    <description>The latest articles on DEV Community by Filipe Nanclarez (@filipenanclarez).</description>
    <link>https://dev.to/filipenanclarez</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F819327%2F3ae6d49b-cfc0-406a-a1ee-060eb78e9846.jpeg</url>
      <title>DEV Community: Filipe Nanclarez</title>
      <link>https://dev.to/filipenanclarez</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/filipenanclarez"/>
    <language>en</language>
    <item>
      <title>Sign in with Apple no Windows com Flutter</title>
      <dc:creator>Filipe Nanclarez</dc:creator>
      <pubDate>Mon, 02 May 2022 21:17:38 +0000</pubDate>
      <link>https://dev.to/filipenanclarez/sign-in-with-apple-no-windows-com-flutter-12ik</link>
      <guid>https://dev.to/filipenanclarez/sign-in-with-apple-no-windows-com-flutter-12ik</guid>
      <description>&lt;p&gt;Hoje resolvi encarar um desafio que está me consumindo energia a alguns meses já.&lt;/p&gt;

&lt;p&gt;Estou criando um aplicativo multiplataforma, e preciso autenticar meus usuários. Uma forma bem conveniente de fazer isso é através de provedores de autenticação conhecidos, como google, facebook, twitter. Isso é conveniente para o usuário, pois não precisa ficar digitando e-mail e senha e lembrando deles depois.&lt;/p&gt;

&lt;p&gt;O problema é que &lt;a href="https://developer.apple.com/app-store/review/guidelines/#sign-in-with-apple" rel="noopener noreferrer"&gt;a apple exige que se o aplicativo tiver qualquer provedor de autenticação, então ele também deve ter a opção de autenticar pela apple&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ok, então é simples, vamos começar pela autenticação da apple. Aí foi onde o problema começou a ficar mais complexo.&lt;/p&gt;

&lt;p&gt;O suporte para desktop foi lançado a pouco tempo pelo equipe do flutter. Com isso alguns parceiros começaram a lançar seus pacotes tambem. Um pacote que estava muito promissor era o da Invertase. Eles estão trabalhando no &lt;a href="https://invertase.io/blog/announcing-flutterfire-desktop" rel="noopener noreferrer"&gt;suporte completo para do flutterfire para desktop&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Eu aguardei pois sabia que logo logo eles iriam colocar a mão nesse problema, e foi exatamente o que aconteceu. Mas para minha surpresa, eles pularam a parte mais difícil. Eles fizeram a autenticação funcionar em &lt;a href="https://github.com/invertase/flutterfire_desktop/issues/68" rel="noopener noreferrer"&gt;MacOs mas não em Windows&lt;/a&gt;. Segundo eles &lt;a href="https://github.com/invertase/flutter_desktop_webview_auth/issues/18" rel="noopener noreferrer"&gt;não é prioridade para eles fazer isso agora&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Isso me fez voltar nesse problema, e resolver isso de uma vez por todas. Depois de pesquisar vários pacotes no pub.dev e não achar nenhum que faça isso (se vc achar me conte por favor), eu resolvi adotar um recurso técnico alternativo. &lt;/p&gt;

&lt;p&gt;Resolvi fazer uma página web que faça a autenticação e apenas me retorne o e-mail do usuário. E com isso eu faço o resto que preciso no meu aplicativo. Não é a solução mais elegante do mundo, mas me lembro de uma frase que diz 'antes feito que perfeito'. Melhor fazer o possível agora, e futuramente quando for preciso melhorar esse recurso, estudo novamente uma solução mais elaborada.&lt;/p&gt;

&lt;p&gt;Então vamos lá. &lt;/p&gt;

&lt;h1&gt;
  
  
  Configurando o servidor web
&lt;/h1&gt;

&lt;p&gt;Primeiro de tudo, precisamos de um servidor web funcional. Vou usar o mesmo que criamos no meu artigo sobre couchbase. Primeiro vamos instalar o apache.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo yum install nano httpd -y&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Com isso se acessarmos o servidor já teremos a página do apache abrindo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1k25tbqfowra7gv77wid.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1k25tbqfowra7gv77wid.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos criar uma pagina para fazer a autenticação.&lt;/p&gt;

&lt;p&gt;Para manter o foco, vamos usar o WinSCP, e só colocar no servidor os arquivos que precisamos. Então vamos conectar no winSCP.&lt;/p&gt;

&lt;p&gt;Depois de conectar, vamos colocar o arquivo index.html dentro da pasta &lt;code&gt;/var/www/html&lt;/code&gt;. Ao fazer isso, você deve receber um erro de permissão. Esse diretório só pode ser acessado com usuário com permissões administrativas. Para simplificar, vamos configurar o WinSCP para executar o sudo logo após efetuar o login.&lt;/p&gt;

&lt;p&gt;Para isso, vamos alterar a configuração do winSCP com esse comando &lt;code&gt;sudo  /usr/libexec/openssh/sftp-server&lt;/code&gt; ( lembrando que esse procedimento é específico para o CentOS ).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqiyjvyryx5nupesbftj2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqiyjvyryx5nupesbftj2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora podemos fazer uma página html simples só para testar o servidor. Vamos criar um arquivo com o seguinte conteúdo e salvar como &lt;code&gt;/var/www/html/index.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;html&amp;gt;
    teste
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com isso devemos conseguir ver o seguinte resultado:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38hzektghe94z3pszhxf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F38hzektghe94z3pszhxf.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Configurando a autenticação no console de desenvolvedor da apple
&lt;/h1&gt;

&lt;p&gt;Agora antes de começarmos o código para a autenticação, vamos criar as configurações necessárias no console de desenvolvedor da apple.&lt;/p&gt;

&lt;p&gt;Para isso estou seguindo &lt;a href="https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple" rel="noopener noreferrer"&gt;esse artigo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Então primeiro vamos fazer login no &lt;a href="https://developer.apple.com/account" rel="noopener noreferrer"&gt;portal de desenvolvedor da Apple.&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Nessa página, vamos em &lt;code&gt;Certificates, IDs &amp;amp; Profiles&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F71yyvppotu9umm3hh45q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F71yyvppotu9umm3hh45q.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na tela abaixo, clique no sinal de mais:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F37ejsyelmjmzsn394i2d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F37ejsyelmjmzsn394i2d.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Escolha &lt;code&gt;Services IDs&lt;/code&gt; e continue:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6csxbddyy56d4jckk9q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv6csxbddyy56d4jckk9q.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Preencha a descrição e o identificador. Anote esse identificador pois vamos usar ele mais pra frente.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl48dzpcb0mp0ycs10z7h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl48dzpcb0mp0ycs10z7h.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finalize registrando o aplicativo.&lt;/p&gt;

&lt;p&gt;Agora vamos criar o certificado para a autenticação.&lt;/p&gt;

&lt;p&gt;No menu inicial clique em &lt;code&gt;Keys&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3j9e8id088xhh3hdsek7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3j9e8id088xhh3hdsek7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Coloque um nome para a chave, e marque a opção &lt;code&gt;Sign in with Apple&lt;/code&gt; e depois clique no botão &lt;code&gt;Configure&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9rmcqk63zzzhkjixpctw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9rmcqk63zzzhkjixpctw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na tela de configuração, escolha o app ( no meu caso, o app já existia, então estou só fazendo a configuração para um app existente ).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fakeq46rhkz66lg6429j8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fakeq46rhkz66lg6429j8.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora que já terminamos de configurar a chave, vamos seguir na tela que estávamos:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzclz1qgsa06ng9855nuw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzclz1qgsa06ng9855nuw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora confirme o registro na tela final:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl0dxu0dwstdkuybab6es.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl0dxu0dwstdkuybab6es.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ATENÇÃO!&lt;/p&gt;

&lt;p&gt;Nessa tela abaixo, anote o ID da chave, pois vamos usar depois. Aqui nós vamos fazer o download do arquivo, que só pode ser feito uma vez, então &lt;strong&gt;ESCOLHA BEM ONDE SALVAR ESSE ARQUIVO&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0tvwzfkyhe2rdq9t7ox2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0tvwzfkyhe2rdq9t7ox2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Embora esteja com a extensão &lt;code&gt;p8&lt;/code&gt; esse é um arquivo de texto. Vamos usar ele na mais à frente.&lt;/p&gt;

&lt;p&gt;Depois de baixar o arquivo, pode finalizar no botão &lt;code&gt;Done&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Configurando autenticação no console do Firebase
&lt;/h1&gt;

&lt;p&gt;Agora vamos no console do firebase e dentro de &lt;code&gt;Authentication&lt;/code&gt; vamos na aba &lt;code&gt;Sign-in method&lt;/code&gt; e depois em &lt;code&gt;Adicionar novo fornecedor&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4dpafq0puv5wwzxz10l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo4dpafq0puv5wwzxz10l.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Escolha apple, e depois vamos adicionar as configurações solicitadas.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Service ID&lt;/code&gt; é o Service ID identifier que anotamos antes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fov07ox9l3bvx4hud99i9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fov07ox9l3bvx4hud99i9.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Team ID&lt;/code&gt; é o código que aparece no topo do console de desenvolvedor da apple:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqiyfe393myc98ofa48ks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqiyfe393myc98ofa48ks.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ID da chave&lt;/code&gt; é o &lt;code&gt;Key ID&lt;/code&gt; que apareceu na etapa de download da chave:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxg1eeg5y96sloh9girdr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxg1eeg5y96sloh9girdr.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;E agora a na chave privada, vamos abrir aquele arquivo que baixamos, e copiar o conteúdo inteiro dentro do campo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2tj026b2dj07rs5qdhay.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2tj026b2dj07rs5qdhay.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora copie a url de retorno para a próxima etapa.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbkqedyni36ilf7hdb3et.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbkqedyni36ilf7hdb3et.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Configurando a url de retorno
&lt;/h1&gt;

&lt;p&gt;Volte no console de desenvolvedor da apple, e na aba &lt;code&gt;Identifiers&lt;/code&gt; clique no menu &lt;code&gt;App IDs&lt;/code&gt; à direita e escolha &lt;code&gt;Services IDs&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F16cyrf2gcs5bbrsf6jjz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F16cyrf2gcs5bbrsf6jjz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Escolha o serviço que criamos, e na tela de detalhes escolha o botão &lt;code&gt;Configure&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnnis7m0nsmvfh8poc7as.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnnis7m0nsmvfh8poc7as.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Escolha o seu app no combo &lt;code&gt;Primary App ID&lt;/code&gt; e preencha seu domínio e a url de retorno que acabamos de copiar do console do firebase:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fayavbl56731gv1krxbop.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fayavbl56731gv1krxbop.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Com isso podemos voltar para o código para fazer a autenticação.&lt;/p&gt;

&lt;h1&gt;
  
  
  Authenticando com apple via firebase com javascript
&lt;/h1&gt;

&lt;p&gt;Como o que queremos é apenas uma página que será chamada dentro de um webview, não iremos instalar o sdk via npm, nem fazer nenhuma configuração mais elaborada. Vamos apenas chamar os pacotes do firebase diretamente no nosso script e disparar o fluxo de autenticação.&lt;/p&gt;

&lt;p&gt;Mas vamos precisar tomar cuidado, para não expor os dados da nosso projeto do firebase no nosso script, pois essa página será pública então vamos encapsular isso usando funções.&lt;/p&gt;

&lt;p&gt;Seguindo a &lt;a href="https://firebase.google.com/docs/web/alt-setup" rel="noopener noreferrer"&gt;documentação do firebase&lt;/a&gt;, para importar os pacotes vamos usar o seguinte código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="c1"&gt;// Import the functions you need from the SDKs you need&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initializeApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.gstatic.com/firebasejs/9.7.0/firebase-app.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getAnalytics&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.gstatic.com/firebasejs/9.7.0/firebase-analytics.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// TODO: Add SDKs for Firebase products that you want to use&lt;/span&gt;
    &lt;span class="c1"&gt;// https://firebase.google.com/docs/web/setup#available-libraries&lt;/span&gt;
    &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;OAuthProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getAuth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signInWithRedirect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getRedirectResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signOut&lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.gstatic.com/firebasejs/9.7.0/firebase-auth.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depois disso, precisamos inicializar o firebase com as variáveis do nosso projeto. Vamos criar uma função e passar essas variáveis como parâmetros, assim elas não ficam expostas. Como o &lt;code&gt;import&lt;/code&gt; dos pacotes só pode ser feito dentro de módulos, e funções dentro de módulos não ficam disponíveis para acesso externo, vamos expor a função jogando ela dentro da variável global &lt;code&gt;window&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Dentro da nossa função, passamos os parâmetros para uma variável, e depois chamamos a inicialização do firebase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;databaseURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;storageBucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;messagingSenderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;measurementId&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="c1"&gt;//Your web app's Firebase configuration&lt;/span&gt;
    &lt;span class="c1"&gt;// For Firebase JS SDK v7.20.0 and later, measurementId is optional&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firebaseConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;databaseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;databaseURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;storageBucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;storageBucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;messagingSenderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;messagingSenderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;measurementId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;measurementId&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;analytics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAnalytics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora que já temos o firebase inicializado, vamos disparar o fluxo de autenticação. Para isso também vamos criar uma função para podemos disparar manualmente em nosso aplicativo flutter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Start redirect auth flow&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;signOutResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;signOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sign-out result:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signOutResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// start auth with redirect&lt;/span&gt;
    &lt;span class="nf"&gt;signInWithRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso vai fazer a página abrir a tela de autenticação da apple. Depois que o usuário se autenticar, o fluxo vai chamar novamente nossa página. Mas agora, vamos precisar pegar o resultado da autenticação. Então para isso vamos criar outra função, que vai pegar essas informações recebidas após o fluxo de autenticação retornar.&lt;/p&gt;

&lt;p&gt;Para enviar uma informação do nosso código javascript para o aplicativo, vamos usar esse código:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;window.chrome.webview.postMessage({chave:valor})&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Depois veremos como receber isso do lado do flutter.  &lt;/p&gt;

&lt;p&gt;Então nossa função para receber o retorno do fluxo de autenticação ficou assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Get result from redirect auth flow&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="nf"&gt;getRedirectResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webview&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;result&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)});&lt;/span&gt;

    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;erro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webview&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;erro ao capturar credencial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)});&lt;/span&gt;

    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adicionando algumas variáveis de controle, um detalhe aqui outro ali, temos o código completo abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Import the functions you need from the SDKs you need&lt;/span&gt;
            &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initializeApp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.gstatic.com/firebasejs/9.7.0/firebase-app.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getAnalytics&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.gstatic.com/firebasejs/9.7.0/firebase-analytics.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// TODO: Add SDKs for Firebase products that you want to use&lt;/span&gt;
            &lt;span class="c1"&gt;// https://firebase.google.com/docs/web/setup#available-libraries&lt;/span&gt;
            &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;OAuthProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getAuth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signInWithRedirect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getRedirectResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signOut&lt;/span&gt;  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.gstatic.com/firebasejs/9.7.0/firebase-auth.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

            &lt;span class="c1"&gt;// Initialize Firebase&lt;/span&gt;
            &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;databaseURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;storageBucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;messagingSenderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;measurementId&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
                &lt;span class="c1"&gt;//Your web app's Firebase configuration&lt;/span&gt;
                &lt;span class="c1"&gt;// For Firebase JS SDK v7.20.0 and later, measurementId is optional&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firebaseConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;authDomain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;databaseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;databaseURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;storageBucket&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;storageBucket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;messagingSenderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;messagingSenderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;measurementId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;measurementId&lt;/span&gt;
                &lt;span class="p"&gt;};&lt;/span&gt;

                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseConfig&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;analytics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAnalytics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OAuthProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;apple.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;firebase init successfully&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="c1"&gt;// Start redirect auth flow&lt;/span&gt;
            &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sign&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
                &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;signOutResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;signOut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sign-out result:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signOutResult&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                &lt;span class="c1"&gt;// start auth with redirect&lt;/span&gt;
                &lt;span class="nf"&gt;signInWithRedirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="c1"&gt;// Get result from redirect auth flow&lt;/span&gt;
            &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
                &lt;span class="nf"&gt;getRedirectResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

                    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

                    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webview&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;result&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)});&lt;/span&gt;

                &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;erro&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webview&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;erro ao capturar credencial&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)});&lt;/span&gt;

                &lt;span class="p"&gt;});&lt;/span&gt;

            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Interagindo com o webview no flutter
&lt;/h1&gt;

&lt;p&gt;Agora vamos para o lado 'dart' da força. kkk&lt;/p&gt;

&lt;p&gt;Trocadilhos a parte, vamos iniciar um novo projeto flutter, e vamos adicionar o plugin &lt;a href="https://pub.dev/packages/webview_windows" rel="noopener noreferrer"&gt;webview_windows&lt;/a&gt; e depois abrir ele no Visual Studio Code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter create &lt;span class="nt"&gt;--platforms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;windows auth_apple_no_windows
&lt;span class="nb"&gt;cd &lt;/span&gt;auth_apple_no_windows
flutter pub add webview_windows
code &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vamos: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://miajimyu.com/en/docs/flutter/flutter-remove-all-comments-in-starter-app/" rel="noopener noreferrer"&gt;remover todos os comentários&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;remover a função de incremento que veio no exemplo padrão&lt;/li&gt;
&lt;li&gt;remover o float button &lt;/li&gt;
&lt;li&gt;declarar um &lt;code&gt;controller&lt;/code&gt; para nosso webview
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebviewController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;incluir o webview no corpo da nossa tela:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Webview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;permissionRequested:&lt;/span&gt; &lt;span class="n"&gt;_onPermissionRequested&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código deverá ficar assim então:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/material.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:webview_windows/webview_windows.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;key:&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MaterialApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;'Flutter Demo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;theme:&lt;/span&gt; &lt;span class="n"&gt;ThemeData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;primarySwatch:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;home:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyHomePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;'Flutter Demo Home Page'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyHomePage&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatefulWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyHomePage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;key:&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyHomePage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_MyHomePageState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_MyHomePageState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyHomePage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebviewController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Webview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora precisamos fazer o webview carregar nossa página assim que essa tela for carregada. Para isso vamos usar uma &lt;a href="https://www.flutterbeads.com/call-method-after-build-in-flutter/" rel="noopener noreferrer"&gt;função de retorno que é chamada quando o último frame da tela é desenhado&lt;/a&gt;, também conhecida como &lt;code&gt;addPostFrameCallback&lt;/code&gt;. Vamos adicionar a chamada dessa função de retorno no método &lt;code&gt;initState&lt;/code&gt; do ciclo de vida da tela:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebviewController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;WidgetsBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;addPostFrameCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// do something&lt;/span&gt;

    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora dentro dessa função, vamos inicializar o controlador e pedir para o webview carregar a página que criamos. :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;    &lt;span class="n"&gt;WidgetsBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;addPostFrameCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;loadUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'http://example.com'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se você tentar rodar o aplicativo nesse momento verá apenas uma tela em branco. Isso porque não disparamos aquelas funções que criamos no javascript.&lt;/p&gt;

&lt;p&gt;Então vamos fazer isso através do método &lt;code&gt;executeScript&lt;/code&gt;. Mas só podemos fazer isso, depois que a página já estiver totalmente carregada. Para isso, vamos adicionar um 'ouvinte', que vai ficar monitorando o webview e vai nos avisar a cada mudança que ocorrer nele. A mudança que nos interessa é a &lt;code&gt;LoadingState.navigationCompleted&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Nessa situação, vamos chamar a função &lt;code&gt;initApp()&lt;/code&gt; passando os parâmetros para iniciar o firebase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;      &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;loadingState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;LoadingState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;navigationCompleted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// inicializando o firebase&lt;/span&gt;
          &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'''
          initApp(
              "AIzaSXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXMA",
              "XXXXXXXXXXX.firebaseapp.com",
              "https://XXXXXXXXX.firebaseio.com",
              "XXXXXXXXXX",
              "XXXXXXXX.appspot.com",
              "96XXXXXXXX19",
              "1:96XXXXXXXXXX19:web:f9XXXXXXXXXXa30XXXXXX6",
              "G-EXXXXXXXX3"
          );
          '''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora que já temos o firebase inicializado, podemos chamar a função que criamos que dispara o login. Podemos fazer isso logo após a inicialização. Não se preocupe, vou postar o código inteiro no final para ficar claro onde exatamente esses pedaços de código estão.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'sign();'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com isso ao executar o aplicativo já irá aparecer a tela de autenticação:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7i78drx5cx9nc4s08ot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7i78drx5cx9nc4s08ot.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mas se vc tentar autenticar agora, encontrará um problema. Como estamos disparando a autenticação assim que a página é carregada, após o fluxo de autenticação terminar e o sistema do firebase redirecionar a página de volta para nós, nosso sistema vai disparar novamente a função de autenticação gerando um loop infinito. &lt;/p&gt;

&lt;p&gt;Para resolver isso de forma simples, vamos criar uma variável de controle que inicia com valor &lt;code&gt;true&lt;/code&gt;. Depois de rodarmos a primeira vez mudamos ela para &lt;code&gt;false&lt;/code&gt; e verificamos ela ao disparar a função de autenticação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;firstRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;firstRun&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;firstRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'sign();'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora o que precisamos é capturar o retorno do fluxo de autenticação quando o usuário terminar de fazer login. Como isso só acontece na segunda vez que a página é carregada, vamos usar esse mesmo &lt;code&gt;if&lt;/code&gt; e chamar a função que captura o resultado no &lt;code&gt;else&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;firstRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;firstRun&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;firstRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'sign();'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'getResult();'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E para receber o valor que essa função envia para nós, vamos usar outro 'ouvinte' que vai ficar monitorando todas as mensagens que recebermos do webview. Lembra que eu disse que ia mostrar como recebemos no dart a mensagem que enviamos lá no código javascript? É aqui que isso acontece:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;webMessage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'result'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'result'&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="s"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="n"&gt;debugPrint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'uid'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="n"&gt;debugPrint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E finalmente o resultado tão esperado. Recebemos o id do usuário lá do firebase, e também o endereço de e-mail dele:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftp3qianq48waqzro49ee.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftp3qianq48waqzro49ee.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora é só usar esses dados da forma que for melhor para o seu projeto.&lt;/p&gt;

&lt;p&gt;Talvez você esteja se perguntando como fica a parte de controle da sessão do usuário e logout e onde vai guardar isso. Isso tudo não faz parte do escopo desse artigo. Eu tentei capturar as credenciais e colocar elas dentro de um objeto de autenticação do lado do aplicativo com o método &lt;code&gt;reauthenticateWithCredential&lt;/code&gt; conforme &lt;a href="https://firebase.flutter.dev/docs/auth/manage-users/#re-authenticate-a-user" rel="noopener noreferrer"&gt;essa documentação&lt;/a&gt;. Mas o package &lt;code&gt;flutter_desktop_webview_auth&lt;/code&gt; da invertase entrou em conflito com o package &lt;code&gt;webview_windows&lt;/code&gt; então eu decidi apenas usar os dados do usuário e fazer o meu próprio controle manualmente mesmo.&lt;/p&gt;

&lt;p&gt;O pessoal da &lt;a href="https://www.back4app.com" rel="noopener noreferrer"&gt;back4app&lt;/a&gt; me sugeriu uma solução parecida com essa que abordamos nesse artigo, mas em vez de usar uma página com javascript, eles indicaram usar o recurso &lt;a href="https://docs.parseplatform.org/cloudcode/guide/#cloud-functions" rel="noopener noreferrer"&gt;Cloud Functions&lt;/a&gt; da plataforma deles.&lt;/p&gt;

&lt;p&gt;Vou finalizar deixando o código dart completo conforme prometi.&lt;/p&gt;

&lt;p&gt;Até a próxima 😉&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/material.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:webview_windows/webview_windows.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyApp&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatelessWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;key:&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;MaterialApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;'Flutter Demo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;theme:&lt;/span&gt; &lt;span class="n"&gt;ThemeData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;primarySwatch:&lt;/span&gt; &lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;home:&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyHomePage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="s"&gt;'Flutter Demo Home Page'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyHomePage&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;StatefulWidget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyHomePage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;key:&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyHomePage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;createState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_MyHomePageState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_MyHomePageState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyHomePage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebviewController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;firstRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;WidgetsBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;addPostFrameCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;loadingState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;LoadingState&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;navigationCompleted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// inicializando o firebase&lt;/span&gt;
          &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'''
          initApp(
              "AIXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXmMA",
              "XXXXXXX.firebaseapp.com",
              "https://XXXXXXX.firebaseio.com",
              "XXXXXXX",
              "XXXXXXX.appspot.com",
              "96XXXXXXX19",
              "1:96XXXXXXX19:web:f9XXXXXXXd6",
              "G-EXXXXXXX3"
          );
        '''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;firstRun&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;firstRun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'sign();'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;executeScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'getResult();'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;webMessage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'result'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;userData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'result'&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="s"&gt;'user'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

          &lt;span class="n"&gt;debugPrint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'uid'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
          &lt;span class="n"&gt;debugPrint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_controller&lt;/span&gt;
          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;loadUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'http://example.com'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="n"&gt;Widget&lt;/span&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BuildContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Scaffold&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;appBar:&lt;/span&gt; &lt;span class="n"&gt;AppBar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;title:&lt;/span&gt; &lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;widget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;Webview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;_controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>apple</category>
      <category>auth</category>
      <category>flutter</category>
      <category>windows</category>
    </item>
    <item>
      <title>Aumentando sua instância Ubuntu no Lightsail</title>
      <dc:creator>Filipe Nanclarez</dc:creator>
      <pubDate>Tue, 22 Mar 2022 02:06:37 +0000</pubDate>
      <link>https://dev.to/filipenanclarez/aumentando-sua-instancia-ubuntu-no-lightsail-5h6f</link>
      <guid>https://dev.to/filipenanclarez/aumentando-sua-instancia-ubuntu-no-lightsail-5h6f</guid>
      <description>&lt;p&gt;Hoje precisei fazer um upgrade de um dos sites que tenho hospedado no LightSail. O processo inicial é documentado pela Amazon, mas na prática, a parte que eu mais precisava não estava na documentação. Então depois de pesquisar um pouco, resolvi compartilhar com a comunidade para facilitar.&lt;/p&gt;

&lt;p&gt;Segundo a documentação &lt;a href="https://lightsail.aws.amazon.com/ls/docs/en_us/articles/how-to-create-larger-instance-from-snapshot-using-console"&gt;nesse link&lt;/a&gt; o que precisamos fazer é criar um snapshot, e depois subir uma outra instância nova a partir desse snapshot.&lt;/p&gt;

&lt;p&gt;Então a partir da página inicial do console do LightSail vamos escolher a instância que queremos aumentar e vamos acessar a opção &lt;code&gt;Gerenciar&lt;/code&gt; do menu:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y4s6edxW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/78a1fm19edt56haseiuq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y4s6edxW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/78a1fm19edt56haseiuq.png" alt="Image description" width="806" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na página da instância, vamos na aba &lt;code&gt;snapshots&lt;/code&gt; e vamos usar a opção &lt;code&gt;Criar snapshot&lt;/code&gt;. Vamos deixar o nome que ele mesmo sugeriu, e depois cliar em &lt;code&gt;Criar&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B8YCwsOZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/al7lc6qfj80lpqh2yfzo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B8YCwsOZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/al7lc6qfj80lpqh2yfzo.png" alt="Image description" width="880" height="667"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depois que o snapshot for criado, vamos no menu do snapshot e vamos acessar a opção &lt;code&gt;Criar nova instância&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DRTvexAL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hyo6genrycfbdxlh453r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DRTvexAL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hyo6genrycfbdxlh453r.png" alt="Image description" width="750" height="122"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eKDadQ8h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q2epylm08dejfup891k8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eKDadQ8h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q2epylm08dejfup891k8.png" alt="Image description" width="761" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depois disso vamos apenas dar um nome qualquer e escolher o novo plano que desejamos para essa instância.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dmdXVl42--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ivm6au3j1xqimp19qyjv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dmdXVl42--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ivm6au3j1xqimp19qyjv.png" alt="Image description" width="799" height="863"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Será preciso esperar após a criação. Ela vai aparecer no console como pendente. Após ela constar como em execução, vamos para a parte que não está na documentação.&lt;/p&gt;

&lt;h1&gt;
  
  
  Redirecionando o ip público
&lt;/h1&gt;

&lt;p&gt;Se seu servidor está sendo usando em produção, então você tem um endereço de ip público associado a essa máquina. Então precisamos apontar esse endereço para a nova instância que criamos.&lt;/p&gt;

&lt;p&gt;Para isso, acesse a aba &lt;code&gt;Redes&lt;/code&gt; ( na tela inicial do console, não dentro da instâcia ) e no endereço de IP stático em questão, vá na opção &lt;code&gt;Gerenciar&lt;/code&gt; do menu:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yoVJYbhm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eqbwapmhkq28drhty54s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yoVJYbhm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eqbwapmhkq28drhty54s.png" alt="Image description" width="700" height="1019"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Escolha a opção &lt;code&gt;Desanexar&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lIb8FHRt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/abkmmbq6s4mgymfstc65.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lIb8FHRt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/abkmmbq6s4mgymfstc65.png" alt="Image description" width="756" height="674"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora anexe a nova instância a esse endereço estático:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eETihZEA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d7qtccmvw830u8iujrsr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eETihZEA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d7qtccmvw830u8iujrsr.png" alt="Image description" width="619" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos para o próximo ajuste que não está na documentação:&lt;/p&gt;

&lt;h1&gt;
  
  
  Replicar as regras de firewall para a nova instância
&lt;/h1&gt;

&lt;p&gt;Provavelmente você também tem regras de firewall no seu servidor. Só que quando você cria uma nova instância a partir do snapshot &lt;strong&gt;as regras de firewall não são replicadas!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Então acesse a instância antiga, e na aba &lt;code&gt;Redes&lt;/code&gt; anote todas as regras que estão listadas para replicar depois. Para facilitar vomos abrir duas abas do navegador e com isso podemos só recriar uma por uma manualmente:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ekcDPDfi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/whawg2ajkxa42my9ac3e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ekcDPDfi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/whawg2ajkxa42my9ac3e.png" alt="Image description" width="880" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos para a última parte&lt;/p&gt;

&lt;h1&gt;
  
  
  Aumentando o espaço de disco no Ubuntu
&lt;/h1&gt;

&lt;p&gt;Com exceção de sistemas que precisam de ajustes específicos como o PostgreSQL, a memória e o processador já serão utilizados automaticamente pela nova instância. Mas isso não acontece com o espaço em disco. Embora o disco físico seja maior, o sistema operacional não entende que esse espaço está disponível para ser usado.&lt;/p&gt;

&lt;p&gt;Então precisamos acessar essa instância e ajustar o tamanho da partição. Para isso vamos usar os utilitários &lt;code&gt;fdisk&lt;/code&gt; e o &lt;code&gt;resize2fs&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Primeiro vamos usar o fdisk para apagar a partição atual e criar uma nova com o mesmo setor de inicio.&lt;/p&gt;

&lt;p&gt;Então primeiro vamos acessar o fdisk e depois vamos usar o comando &lt;code&gt;p&lt;/code&gt; para listar as partições:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fdisk /dev/xvda

Welcome to fdisk &lt;span class="o"&gt;(&lt;/span&gt;util-linux 2.25.2&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;                                                                                                                                                           
Changes will remain &lt;span class="k"&gt;in &lt;/span&gt;memory only, &lt;span class="k"&gt;until &lt;/span&gt;you decide to write them.
Be careful before using the write command.

Command &lt;span class="o"&gt;(&lt;/span&gt;m &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;: p
Disk /dev/xvda: 20 GiB, 21474836480 bytes, 41943040 sectors
Units: sectors of 1 &lt;span class="k"&gt;*&lt;/span&gt; 512 &lt;span class="o"&gt;=&lt;/span&gt; 512 bytes
Sector size &lt;span class="o"&gt;(&lt;/span&gt;logical/physical&lt;span class="o"&gt;)&lt;/span&gt;: 512 bytes / 512 bytes
I/O size &lt;span class="o"&gt;(&lt;/span&gt;minimum/optimal&lt;span class="o"&gt;)&lt;/span&gt;: 512 bytes / 512 bytes
Disklabel &lt;span class="nb"&gt;type&lt;/span&gt;: dos
Disk identifier: 0xd7f2e0e8

Device     Boot Start      End  Sectors Size Id Type
/dev/xvda1 &lt;span class="k"&gt;*&lt;/span&gt;     4096 16773119 16769024   8G 83 Linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repare no campo &lt;code&gt;Start&lt;/code&gt; que está em 4096. Vamos criar a nova partição iniciando nesse mesmo ponto.&lt;/p&gt;

&lt;p&gt;Ainda no modo interativo do fdisk, use a opção &lt;code&gt;d&lt;/code&gt; para excluir a partição:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command &lt;span class="o"&gt;(&lt;/span&gt;m &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;: d
Selected partition 1
Partition 1 has been deleted.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vamos criar a nova partição usando o comando &lt;code&gt;n&lt;/code&gt;. Vamos preencher o primeiro setor com o valor que anotamos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command &lt;span class="o"&gt;(&lt;/span&gt;m &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;: n
Partition &lt;span class="nb"&gt;type
   &lt;/span&gt;p   primary &lt;span class="o"&gt;(&lt;/span&gt;0 primary, 0 extended, 4 free&lt;span class="o"&gt;)&lt;/span&gt;
   e   extended &lt;span class="o"&gt;(&lt;/span&gt;container &lt;span class="k"&gt;for &lt;/span&gt;logical partitions&lt;span class="o"&gt;)&lt;/span&gt;
Select &lt;span class="o"&gt;(&lt;/span&gt;default p&lt;span class="o"&gt;)&lt;/span&gt;: p
Partition number &lt;span class="o"&gt;(&lt;/span&gt;1-4, default 1&lt;span class="o"&gt;)&lt;/span&gt;: 
First sector &lt;span class="o"&gt;(&lt;/span&gt;2048-41943039, default 2048&lt;span class="o"&gt;)&lt;/span&gt;: 4096
Last sector, +sectors or +size&lt;span class="o"&gt;{&lt;/span&gt;K,M,G,T,P&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;4096-41943039, default 41943039&lt;span class="o"&gt;)&lt;/span&gt;: 

Created a new partition 1 of &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s1"&gt;'Linux'&lt;/span&gt; and of size 20 GiB.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depois disso vamos marcar ela como inicializável com o comando &lt;code&gt;a&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command &lt;span class="o"&gt;(&lt;/span&gt;m &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;: a
Selected partition 1
The bootable flag on partition 1 is enabled now.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vamos usar o comando &lt;code&gt;p&lt;/code&gt; para verificar se está tudo ok:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command &lt;span class="o"&gt;(&lt;/span&gt;m &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;: p
Disk /dev/xvda: 20 GiB, 21474836480 bytes, 41943040 sectors
Units: sectors of 1 &lt;span class="k"&gt;*&lt;/span&gt; 512 &lt;span class="o"&gt;=&lt;/span&gt; 512 bytes
Sector size &lt;span class="o"&gt;(&lt;/span&gt;logical/physical&lt;span class="o"&gt;)&lt;/span&gt;: 512 bytes / 512 bytes
I/O size &lt;span class="o"&gt;(&lt;/span&gt;minimum/optimal&lt;span class="o"&gt;)&lt;/span&gt;: 512 bytes / 512 bytes
Disklabel &lt;span class="nb"&gt;type&lt;/span&gt;: dos
Disk identifier: 0xd7f2e0e8

Device     Boot Start      End  Sectors Size Id Type
/dev/xvda1 &lt;span class="k"&gt;*&lt;/span&gt;     4096 41943039 41938944  20G 83 Linux
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora que o campo &lt;code&gt;Size&lt;/code&gt; já aparece com o tamanho novo, iremos gravar as alterações com o comando &lt;code&gt;w&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command &lt;span class="o"&gt;(&lt;/span&gt;m &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;: w
The partition table has been altered.
Calling ioctl&lt;span class="o"&gt;()&lt;/span&gt; to re-read partition table.
Re-reading the partition table failed.: Device or resource busy

The kernel still uses the old table. The new table will be used at the next reboot or after you run partprobe&lt;span class="o"&gt;(&lt;/span&gt;8&lt;span class="o"&gt;)&lt;/span&gt; or kpartx&lt;span class="o"&gt;(&lt;/span&gt;8&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Opa! Aqui temos um problema. O sistema não vai conseguir fazer a gravação porque essa unidade está montada. O que vamos precisar fazer é dizer para o sistema que queremos que ele aplique essas alterações na próxima vez que o sistema reiniciar. Isso é bem simples, basta criarmos um arquivo com o nome &lt;code&gt;forcefsck&lt;/code&gt; na raiz do nosso disco para que o sistema faça exatamente o que precisamos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo touch&lt;/span&gt; /forcefsck
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vamos reiniciar o servidor e verificar o que aconteceu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ao reiniciar você provavalmente perceberá que o comando &lt;code&gt;df -h&lt;/code&gt; continua mostrando o tamanho antigo. Isso acontece porque aumentamos a partição mas não aumentamos o tamanho registrado no sistema de arquivos.&lt;/p&gt;

&lt;p&gt;Para corrigirmos isso, vamos utilizar o comando &lt;code&gt;resize2fs&lt;/code&gt; que faz exatamente isso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;resize2fs /dev/xvda1
resize2fs 1.42.13 &lt;span class="o"&gt;(&lt;/span&gt;17-May-2015&lt;span class="o"&gt;)&lt;/span&gt;
Filesystem at /dev/xvda1 is mounted on /&lt;span class="p"&gt;;&lt;/span&gt; on-line resizing required
old_desc_blocks &lt;span class="o"&gt;=&lt;/span&gt; 2, new_desc_blocks &lt;span class="o"&gt;=&lt;/span&gt; 3
The filesystem on /dev/xvda1 is now 10485504 &lt;span class="o"&gt;(&lt;/span&gt;4k&lt;span class="o"&gt;)&lt;/span&gt; blocks long.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora ao executar o comando &lt;code&gt;df-h&lt;/code&gt; temos o novo tamanho disponível para uso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;df&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;
Filesystem      Size  Used Avail Use% Mounted on
udev            488M     0  488M   0% /dev
tmpfs           100M  3.1M   97M   4% /run
/dev/xvda1       39G   16G   24G  40% /
tmpfs           496M     0  496M   0% /dev/shm
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           496M     0  496M   0% /sys/fs/cgroup
tmpfs           100M     0  100M   0% /run/user/1000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusão.
&lt;/h1&gt;

&lt;p&gt;Como vimos hoje, não basta apenas migrar seu servidor para uma instância maior. É preciso concluir mais algumas etapas para completar essa migração e ter o servidor realmente funcionando em uma instância com mais recursos. Vimos como fazer isso em uma instância com sistema operacional Ubuntu hospedada no serviço LightSail da plataforma AWS da Amazon.&lt;/p&gt;

&lt;p&gt;Nos vemos na próxima!&lt;/p&gt;

&lt;p&gt;😉&lt;/p&gt;

</description>
      <category>lightsail</category>
      <category>ubuntu</category>
      <category>resize2fs</category>
      <category>fdisk</category>
    </item>
    <item>
      <title>Protegendo a comunicação do Flutter com o Couchbase</title>
      <dc:creator>Filipe Nanclarez</dc:creator>
      <pubDate>Sat, 26 Feb 2022 19:12:04 +0000</pubDate>
      <link>https://dev.to/filipenanclarez/protegendo-a-comunicacao-do-flutter-com-o-couchbase-19jo</link>
      <guid>https://dev.to/filipenanclarez/protegendo-a-comunicacao-do-flutter-com-o-couchbase-19jo</guid>
      <description>&lt;p&gt;No primeiro artigo dessa série fizemos uma POC simples mostrando uma alternativa ao Firestore para um aplicativo offline-first construido com flutter.&lt;/p&gt;

&lt;p&gt;No segundo artigo instalamos um servidor Couchbase na nuvem usando o serviço Lightsail da Amazon.&lt;/p&gt;

&lt;p&gt;Neste artigo iremos configurar o servidor para exigir usuário e senha via TLS (https/wss) e ajustar nosso aplicativo pra funcionar com essa autenticação sem expôr esses em nosso código dart.&lt;/p&gt;

&lt;h2&gt;
  
  
  Índice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Vamos falar de segurança&lt;/li&gt;
&lt;li&gt;Habilitando autenticação no Couchbase Gateway&lt;/li&gt;
&lt;li&gt;Configurando TLS (https/wss)&lt;/li&gt;
&lt;li&gt;Restringindo os dados&lt;/li&gt;
&lt;li&gt;Conclusão&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conforme descrito na &lt;a href="https://docs.couchbase.com/sync-gateway/current/authentication-users.html" rel="noopener noreferrer"&gt;documentação&lt;/a&gt; há três formas de autenticar o acesso de usuários em nosso Couchbase Gateway. &lt;/p&gt;

&lt;p&gt;1 - Autenticação anônima&lt;br&gt;
2 - Autenticação básica&lt;br&gt;
3 - Autenticação usando provedores externos&lt;/p&gt;

&lt;p&gt;Desnecessário dizer que se você quer um alto nível de segurança deverá estudar como implantar a terceira opção.&lt;/p&gt;

&lt;p&gt;Mas para o nosso projeto vamos usar a segunda opção e aproveitar para abordar um tema bem interessante.&lt;/p&gt;
&lt;h3&gt;
  
  
  Vamos falar de segurança&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Sempre que estudo conteúdo sobre segurança chego no mesmo conceito. Nada é 100% seguro! Geralmente as boas práticas de segurança tem o objetivo de adicionar barreiras para tornar os ataques mais difíceis para um invasor. A quantidade dessas barreiras e o nível de complexidade delas vai depender do seu orçamento. No final das contas é como uma competição de quem tem mais recursos. Você precisa ter mais recursos do que seu inimigo. Ou seja, se seu aplicativo é um aplicativo de banco, com certeza você vai precisar gastar um bom tempo e dinheiro com soluções de segurança de alto nível.&lt;/p&gt;

&lt;p&gt;Mas não somos um banco. E há muitos serviços que precisamos acessar que não possuem esse alto nível de segurança em suas estruturas. Muitos serviços de terceiros usam uma autenticação básica em que devemos enviar um token, ou então um usuário e uma senha para validar nosso acesso.&lt;/p&gt;

&lt;p&gt;Então vamos fazer a pergunta fatídica: Como usar chaves de API de serviços de terceiros de maneira relativamente segura usando os recursos que temos. Combinamos que não iríamos usar Firebase lembra? Então não iremos usar o Remote Config. Não é que a solução deles seja ruim, mas se você estiver desenvolvendo um aplicativo para Windows terá dificuldade de implementar isso enquanto a &lt;a href="https://invertase.io/" rel="noopener noreferrer"&gt;INVERTASE&lt;/a&gt; não finalizar o &lt;a href="https://invertase.io/blog/announcing-flutterfire-desktop" rel="noopener noreferrer"&gt;desenvolvimento do flutterfire em dart puro&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Então voltando a nossa pergunta, se procurar na internet encontrará vários posts com respostas sobre isso. Mas eu recomendo a leitura &lt;a href="https://www.androidauthority.com/how-to-hide-your-api-key-in-android-600583/" rel="noopener noreferrer"&gt;desse artigo&lt;/a&gt; que aborda o assunto com a devida profundidade. Nele o autor descreve algumas formas de fazer isso e quais são as melhoras.&lt;/p&gt;

&lt;p&gt;Segundo a &lt;a href="https://docs.flutter.dev/deployment/cd#cloud-build-and-deploy-setup" rel="noopener noreferrer"&gt;documentação oficial&lt;/a&gt; do flutter, podemos usar variáveis de ambiente preenchidas em tempo de compilação para guardar informações sensíveis.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Continuous Integration (CI) systems generally support encrypted environment variables to store private data. You can pass these environment variables using --dart-define MY_VAR=MY_VALUE while building the app.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Isso fará com que esses valores sejam preenchidos apenas no momento que você compila o seu aplicativo.&lt;/p&gt;

&lt;p&gt;Então vamos usar essas variáveis para guardar nossa chave, e fazer algumas verificações simples pra confirmar que isso que estamos fazendo traz um nível mínimo de dificuldade para um possível curioso tentando pegar nosso token.&lt;/p&gt;

&lt;p&gt;Dentro do código você pode acessar essas variáveis com o seguinte código:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const String apiSecret = String.fromEnvironment('MY_VAR');&lt;/code&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Atenção:&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;&lt;strong&gt;Você precisa atribuir o valor a uma variável do tipo &lt;code&gt;const&lt;/code&gt; caso contrário o dart não irá atribuir o valor corretamente.&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Vamos fazer isso e ver o que acontece. Vou adicionar esse código no inicio do aplicativo que criamos no primeiro artigo. Se eu criar uma variável e nunca usar ela, o processo de compilação remove o código não usado do aplicativo final, então para que nosso teste funcione vou usar um &lt;code&gt;print()&lt;/code&gt; logo após receber a variável:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrrec779zg1bojtqolqa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrrec779zg1bojtqolqa.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos gerar um .apk passando um valor para essa variável no momento da compilação e vamos ver onde e como ela aparecerá para alguém tentando fazer engenharia reversa em nosso aplicativo:&lt;/p&gt;

&lt;p&gt;Para gerar o .apk passando as variáveis vamos usar o seguinte comando:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;flutter build apk --dart-define MY_VAR=CH@V3S3KR3T@&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Vamos usar o &lt;a href="https://github.com/skylot/jadx#readme" rel="noopener noreferrer"&gt;jadx&lt;/a&gt; para descompilar o apk e ver o código dele. Para detalhes de como fazer isso recomendo &lt;a href="https://ourcodeworld.com/articles/read/387/how-to-decompile-an-apk-or-dex-file-using-jadx-in-windows" rel="noopener noreferrer"&gt;esse artigo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ao abrir o .apk e procurar nossa chave percebemos que ela não aparece numa busca direta:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mr3o5svgfrqbxek0srz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mr3o5svgfrqbxek0srz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos insistir mais um pouco e tentar achar essa chave diretamente em qualquer arquivo inclusive executáveis. Para isso vou descompactar o .apk em uma pasta e usar o &lt;a href="https://www.mythicsoft.com/agentransack/" rel="noopener noreferrer"&gt;Agent Ransack&lt;/a&gt; para tentar achar esse valor em qualquer arquivo dentro dessa pasta:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm8cd7wrgha1mtwqmch8t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm8cd7wrgha1mtwqmch8t.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pronto, achamos ela. Mas como vimos ela não está associada a nenhum nome de variável, e mesmo olhando código puro ela não aparece com facilidade. Se criarmos uma chave complexa com simbolos e de tamanho considerável será mais dificil ainda para um invasor saber o que encontrar. Podemos também codificar ela com algum algoritmo de nossa escolha como o base64, o que faria com o que o invasor tivesse que analisar a lógica do nosso código pra descobrir o que estamos fazendo pra depois tentar achar a chave e decodificar ela de volta e conseguir finalmente acessar nossa API de terceiros. Também podemos usar essa técnica com a url de acesso da api, o que faria com que o atacante perdesse mais tempo ainda identificando o que precisa decodificar e qual valor é usado em qual variável.&lt;/p&gt;

&lt;p&gt;Isso já traz dificuldade suficiente para o nosso projeto. Como eu disse acima, há outras soluções mais avançadas que você pode implantar se seu projeto precisa de mais proteção.&lt;/p&gt;

&lt;p&gt;Para usar essas variáveis enquanto estiver em modo debug, você precisa adicionar elas na chave &lt;code&gt;toolArgs&lt;/code&gt; do seu arquivo &lt;code&gt;launch.json&lt;/code&gt; conforme descrito &lt;a href="https://dartcode.org/docs/using-dart-define-in-flutter/" rel="noopener noreferrer"&gt;aqui&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;Então primeiro vamos configurar o Couchbase Gateway para exigir autenticação com usuário e senha, e depois vamos ajustar nosso aplicativo para usar essas variáveis em tempo de compilação e enviar os dados para o Couchbase Gateway de maneira segura.&lt;/p&gt;
&lt;h3&gt;
  
  
  Habilitando autenticação no Couchbase Gateway&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Lá no começo falamos das três formas de autenticação que o Couchbase Gateway suporta. &lt;/p&gt;

&lt;p&gt;A autenticação anônima é a que já está sendo usando na instalação padrão que fizemos. Por isso que conseguimos inserir o documento no banco diretamente pelo powershell sem nenhuma senha.&lt;/p&gt;

&lt;p&gt;O que diz para o Couchbase Gateway permitir isso é a seguinte diretiva no arquivo de configurações:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7jh113d3jg5q9q5wshc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7jh113d3jg5q9q5wshc.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Então o que vamos fazer é desativar o acesso anônimo, e adicionar um usuário com senha nessa lista. Nosso arquivo vai ficar assim:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fupgwgjg7gvpaq0139nmp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fupgwgjg7gvpaq0139nmp.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reinicie o serviço e verifique se o console está respondendo corretamente, se não estiver revise o arquivo antes de seguir.&lt;/p&gt;

&lt;p&gt;Agora se tentarmos fazer a inclusão via powershell, devemos receber o erro de login requerido:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc50bb78v443jfiq3qq14.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc50bb78v443jfiq3qq14.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Configurando TLS (https)&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Nada do que fizemos faz sentido se a solicitação estiver sendo enviada em texto puro para o servidor. Então o próximo passo é habilitar a comunicação segura entre o nosso aplicativo e o servidor. Para ativar o &lt;a href="https://en.wikipedia.org/wiki/Transport_Layer_Security" rel="noopener noreferrer"&gt;TLS&lt;/a&gt; precisamos de um certificado válido. E para fazer isso direito precisamos de uma autoridade certificadora (CA) do mundo real em vez de usar um certificado auto-assinado. Para isso vamos usar a &lt;a href="https://letsencrypt.org/pt-br/" rel="noopener noreferrer"&gt;Let's Encrypt&lt;/a&gt; uma CA real que nos permite gerar certificados de forma gratuita.&lt;/p&gt;

&lt;p&gt;Para podermos gerar o certificado precisamos de um domínio válido configurado. Se seu projeto for bem pequeno e você não tem um domnínio ainda, a próxima etapa não se aplica a seu caso.&lt;/p&gt;

&lt;p&gt;Vou acessar o provedor do meu domínio na parte de DNS e apontar o serviço de DNS para a Amazon.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpncmlkxiuxqx78jhuqlj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpncmlkxiuxqx78jhuqlj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depois disso podemos alterar as configurações do DNS diretamente no Lightsail. Acesse o console e na aba Networking, escolha Create DNS zone:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdd8x39v8c2e7hjrrac79.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdd8x39v8c2e7hjrrac79.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora coloque o nome do seu domínio e escolha Create DNS zone:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jg6ymu3kf6k5oz6xze4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jg6ymu3kf6k5oz6xze4.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depois de criado, vamos na opção adcionar registro:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs7wslhw7hc4aw62iq2vm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs7wslhw7hc4aw62iq2vm.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Preecha conforme imagem abaixo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2gze0vdexhnbckc9q80c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2gze0vdexhnbckc9q80c.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depois disso você deverá ser capaz de acessar as APIs usando o seu dominio em vez de usar o ip:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkkkzet5npzgqq5e3hivo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkkkzet5npzgqq5e3hivo.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Se ao tentar acessar seu navegador automaticamente redirecionar para https, você pode desativar isso seguindo &lt;a href="https://howchoo.com/chrome/stop-chrome-from-automatically-redirecting-https" rel="noopener noreferrer"&gt;esse artigo&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Agora que temos o domínio configurado, vamos gerar o certificado.&lt;/p&gt;

&lt;p&gt;Primeiro vamos instalar as dependências:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo yum install epel-release -y&lt;/code&gt; &lt;br&gt;
&lt;code&gt;sudo yum install snapd -y&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;Agora vamos ativar o serviço e habilitar o suporte clássico conforme descrito &lt;a href="https://snapcraft.io/docs/installing-snap-on-centos" rel="noopener noreferrer"&gt;aqui&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo systemctl enable --now snapd.socket&lt;/code&gt;&lt;br&gt;
&lt;code&gt;sudo ln -s /var/lib/snapd/snap /snap&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;Agora vamos instalar o certbot:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo snap install --classic certbot&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos criar os certificados:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo certbot certonly --standalone -d example.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fssthkaqu0h0el9kp7cpz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fssthkaqu0h0el9kp7cpz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ao fazer isso serão criados os seguintes arquivos:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/etc/letsencrypt/live/example.com/fullchain.pem&lt;/code&gt; &lt;br&gt;
&lt;code&gt;/etc/letsencrypt/live/example.com/privkey.pem&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Vamos dar permissão de leitura para esses arquivos:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo chmod -R 755 /etc/letsencrypt&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos configurar o Couchbase Gateway para usar esse certificado. Ao fazer isso ele automaticamente passa a usar TLS na comunicação da API. Conforme descrito &lt;a href="https://docs.couchbase.com/sync-gateway/2.8/authentication-certs.html" rel="noopener noreferrer"&gt;nessa parte da documentação&lt;/a&gt;, devemos adicionar o caminho dos arquivos do certificado e da chave no nosso arquivo de configuração do Couchbase Gateway.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejovy9wiyv717wtwb7ms.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejovy9wiyv717wtwb7ms.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reinicie o serviço e teremos a API respondendo via HTTPS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0us3pdarz3aqx1c60tq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0us3pdarz3aqx1c60tq.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Com isso temos o servidor devidamente configurado para ser acessado através via TLS e exigindo usuário e senha.&lt;/p&gt;

&lt;p&gt;Agora vamos voltar para o flutter e ajustar nosso app para utilizar o usuário e senha que criamos e sincronizar usando TLS.&lt;/p&gt;

&lt;p&gt;O que vamos fazer é o seguinte. Vamos adicionar o parametro &lt;code&gt;authenticator&lt;/code&gt; nas configurações do replicador. Nele vamos passar o usuário e a senha que configuramos no Couchbase Gateway. &lt;/p&gt;

&lt;p&gt;Mas como falamos no começo do artigo, esses valores não iremos digitar direto no código. Vamos trazer eles através das variáveis preenchidas em tempo de execução. Vamos aproveitar e fazer isso também para a url da API do Couachbase Gateway. Também vamos usar base64 para ofuscar elas.&lt;/p&gt;

&lt;p&gt;Então vamos começar declarando e populando as variáveis.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_MyHomePageState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="n"&gt;State&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MyHomePage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;replicator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Aqui recebemos as variáveis que virão do ambiente via dart-define&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;constCouchGwUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'couchGwUser'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;constCouchGwPass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'couchGwPwd'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;constEndPointUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromEnvironment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'endPointUrl'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Aqui decodificamos de base64 para o valor original&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;couchGwUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Utf8Codec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base64Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;constCouchGwUser&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;couchGwPwd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Utf8Codec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base64Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;constCouchGwPwd&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;endPointUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Utf8Codec&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base64Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;constEndPointUrl&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_incrementCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vamos alterar as configurações do replicador. Vamos usar a variável da url do endpint que criamos, e vamos incluir o parâmetro &lt;code&gt;authenticator&lt;/code&gt; passando um objeto do tipo &lt;code&gt;BasicAuthenticator&lt;/code&gt; com o nome de usuário e a senha:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// replicador&lt;/span&gt;
&lt;span class="n"&gt;replicator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Replicator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;ReplicatorConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;database:&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;target:&lt;/span&gt; &lt;span class="n"&gt;UrlEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Uri&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endPointUrl&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="c1"&gt;// authenticador&lt;/span&gt;
    &lt;span class="nl"&gt;authenticator:&lt;/span&gt; &lt;span class="n"&gt;BasicAuthenticator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;username:&lt;/span&gt; &lt;span class="n"&gt;couchGwUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;couchGwPwd:&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O valor das variáveis nós iremos enviar em base64, então vamos codificar esses valores usando o site &lt;a href="https://www.base64encode.org/" rel="noopener noreferrer"&gt;base64encode.org&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;O usuário:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg1p60yk335uqzf13bbt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg1p60yk335uqzf13bbt.png" alt="Image description"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;A senha:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkq1tf4nop549avuyhhyh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkq1tf4nop549avuyhhyh.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;E agora a url do endpoint:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Importante!&lt;/strong&gt;&lt;/em&gt;&lt;br&gt;
Nós precisamos mudar a url porque agora a comunicação será via TSL então em vez de usar o prefixo &lt;code&gt;ws://&lt;/code&gt; vamos usar &lt;code&gt;wss://&lt;/code&gt;:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvjlg2wnevrkxa9ha5xj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvjlg2wnevrkxa9ha5xj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para podermos usar essas variáveis em modo debug vamos alterar o lauch.json da seguinte forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"configurations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"poc_flutter_couchbase"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Variaveis&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;que&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;serão&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;passadas&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;em&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tempo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;de&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;compilação&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"toolArgs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"--dart-define"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"couchGwUser=c3luY19jbGllbnQ="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"--dart-define"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"couchGwPwd=Q0hAVjNTM0tSM1RA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"--dart-define"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="s2"&gt;"endPointUrl=d3NzOi8vZXhhbXBsZS5jb206NDk4NC9teS1kYXRhYmFzZQ=="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"poc_flutter_couchbase (profile mode)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dart"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"flutterMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"profile"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rode o programa e faça o teste, se tudo estiver certo você verá no log que o documento foi incluido e verá no servidor que o documento foi criado com sucesso. &lt;/p&gt;

&lt;h3&gt;
  
  
  Restringindo os dados&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Por último vamos falar de um problema que precisamos eliminar se quisermos usar isso tudo em um aplicativo real. &lt;/p&gt;

&lt;p&gt;Imagine que fizemos tudo isso, e agora desenvolvemos um aplicativo para que os usuários guardem suas anotações pessoais. &lt;/p&gt;

&lt;p&gt;Da forma como o código está, se 10 usuários estiverem usando o aplicativo e incluirem 10 notas, todos os 10 usuários receberão 100 notas durante a sincronização. Ou seja, não há nenhum filtro. Você pode pensar em fazer um filtro no seu código, mas isso não impediriam que os dados fossem enviados para o seu aplicativo, o que sobrecarregaria a conexão do usuário, sem falar na franquia. Só quando todos os dados de todos os usuários chegassem no seu aplicativo é que você descartaria os registros. Imagina isso em escala, para mil usuários, um milhão. Acho que já deu pra entender o problema.&lt;/p&gt;

&lt;p&gt;A solução seria de alguma forma informar ao servidor que durante a sincronização ele só deve nos enviar dados do usuário atual, não de todos os documentos.&lt;/p&gt;

&lt;p&gt;Para isso existe o recurso de 'canais' no couchbase. Quando criamos o replicador podemos dizer quais 'canais' queremos escutar. Com isso o replicador só irá receber documentos desses 'canais' que definirmos.&lt;/p&gt;

&lt;p&gt;No nosso exemplo, podemos criar um canal específico para o usuário, e configurar o replicador para receber somente documentos desse usuário.&lt;/p&gt;

&lt;p&gt;Para isso vamos alterar novamente o código da seguinte forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// replicador&lt;/span&gt;
&lt;span class="n"&gt;replicator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Replicator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;ReplicatorConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;database:&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;target:&lt;/span&gt; &lt;span class="n"&gt;UrlEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Uri&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;endPointUrl&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="nl"&gt;channels:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'userA'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="c1"&gt;// authenticador&lt;/span&gt;
    &lt;span class="nl"&gt;authenticator:&lt;/span&gt; &lt;span class="n"&gt;BasicAuthenticator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;username:&lt;/span&gt; &lt;span class="n"&gt;couchGwUser&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;couchGwPwd:&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com isso estamos dizendo que desejamos receber dados apenas do &lt;code&gt;userA&lt;/code&gt;. É claro que você deve usar uma váriavel aqui que tenha o identificador do seu usuário.&lt;/p&gt;

&lt;p&gt;Depois de fazer isso no replicador precisamos fazer mais uma coisa. Toda vez que enviarmos um documento para o servidor, precisamos registrar no documento a qual canal aquele documento pertence. A configuração de canais é bem flexível, podemos alterar o script do gateway para buscar isso de um campo específico ou usar qualquer lógica que atenda nossa necessidade. Mas por padrão o gateway irá definir o canal do documento usando o campo &lt;code&gt;channels&lt;/code&gt; que aceita uma lista de valores. Então na criação do documento iremos alterar o código da seguinte forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MutableDocument&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="s"&gt;'type'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'logMessage'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;'createdAt'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="s"&gt;'message'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'teste'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;'channels'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'userA'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// canal que esse documento será atribuido&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com isso nosso aplicativo só irá sincronizar dados referente ao usuário &lt;code&gt;userA&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusão&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Nessa série de artigos implementamos uma solução offline-first em Flutter sem usar os serviços do Google Firebase. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Criamos um VPS (Servidor Virtual Privado) na Amazon usando o Lightsail.&lt;/li&gt;
&lt;li&gt;Instalamos o Couchbase Server e o Couchbase Gateway no Centos7.&lt;/li&gt;
&lt;li&gt;Criamos os certificados usando a Let’s Encrypt como CA (Autoridade Certificadora) e ativamos o TLS (Transport Layer Security) para que a comunicação seja feita via https/wss.&lt;/li&gt;
&lt;li&gt;Configuramos a API do Couchbase Gateway para exigir autenticação básica (usuário e senha).&lt;/li&gt;
&lt;li&gt;Usamos o aplicativo de demonstração do Flutter para gravar os dados de modo offline e depois sincronizar esses dados com o Couchbase Gateway.&lt;/li&gt;
&lt;li&gt;Usamos &lt;code&gt;dart-define&lt;/code&gt; para não expor a senha em nosso código.&lt;/li&gt;
&lt;li&gt;Configuramos um canal para filtrar o envio e recebimento dos documentos do nosso usuário.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Não achei muito material abordando esse tema, especialmente em português então espero que esses artigos sejam úteis para quem precisar.&lt;/p&gt;

&lt;p&gt;😉&lt;/p&gt;

</description>
      <category>security</category>
      <category>api</category>
      <category>flutter</category>
      <category>couchbase</category>
    </item>
    <item>
      <title>Couchbase Server e Gateway no AWS Lightsail </title>
      <dc:creator>Filipe Nanclarez</dc:creator>
      <pubDate>Sat, 26 Feb 2022 19:11:29 +0000</pubDate>
      <link>https://dev.to/filipenanclarez/couchbase-server-e-gateway-no-aws-lightsail-1ijl</link>
      <guid>https://dev.to/filipenanclarez/couchbase-server-e-gateway-no-aws-lightsail-1ijl</guid>
      <description>&lt;p&gt;No primeiro artigo dessa série, fizemos uma POC simples gravando dados em uma instância do Couchbase embutida no aplicativo de exemplo padrão do flutter. Além disso instalamos em nosso ambiente local uma versão de servidor do Couchbase e configuramos um gateway para que nosso aplicativo se comunicasse com o servidor. E por fim finalizamos fazendo uma sincronização dos dados do nosso aplicativo para o servidor.&lt;/p&gt;

&lt;p&gt;Nesse artigo vamos fazer a instalação do servidor em um servidor virtual hospedado na Amazon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Índice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Criando o servidor&lt;/li&gt;
&lt;li&gt;Instalando o Couchbase Server&lt;/li&gt;
&lt;li&gt;Instalando o Couchbase Gateway&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Criando o servidor&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;No artigo passado citamos dois tipos de serviço na nuvem. O Lightsail e o EC2. De forma resumida, a diferença entre eles é que o Lightsail simplifica as coisas para quem está começando. Se você decidir usar o EC2 você tem o benefício de poder usar gratuidamente por um ano. Em contrapartida terá que se preocupar com as redes VPS, com o funcionamento do LoadBalancer etc. Usando Lightsail você terá apenas três meses gratuitos, mas terá menos coisas para configurar. Mantendo o conceito do artigo anterior, vamos criar o servidor usando o serviço Lightsail e futuramente se você quiser migrar para o EC2 poderá fazer isso sem maiores problemas.&lt;/p&gt;

&lt;p&gt;Vamos acessar o &lt;a href="https://aws.amazon.com/aws/lightsail" rel="noopener noreferrer"&gt;site oficial&lt;/a&gt; e usar o grande botão laranja bem no meio da tela:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9hxf5qe3qd4tni8f9bsy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9hxf5qe3qd4tni8f9bsy.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Na tela de autenticação ele irá nos perguntar se queremos logar ou queremos criar uma nova conta. Vamos criar uma nova conta:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbubofde31pbkle289isn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbubofde31pbkle289isn.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prencha com seu e-mail e um nome de usuário e prossiga:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyp056j8xpz2xj401su5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyp056j8xpz2xj401su5.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depois das verificações de e-mail e segurança será preciso escolher uma senha.&lt;/p&gt;

&lt;p&gt;Em seguida ele pedirá informações de contato. Não coloque acentos no nome da cidade e do estado. Depois siga para informações de faturamento, e por ultimo confirmação de contato por sms ou ligação de voz.&lt;/p&gt;

&lt;p&gt;Depois disso você poderá escolher o nível de suporte. Vou seguir com o suporte gratuito.&lt;/p&gt;

&lt;p&gt;Por fim, veremos a tela de conclusão do cadastro. Vamos usar o botão laranja novamente para acessar o Console de Gerenciamento da AWS.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn0ng2h7oqgzzf0o0nfpk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn0ng2h7oqgzzf0o0nfpk.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depois de fazer login denovo, caimos na tela principal do console. Mas não vamos usar esse console. Vamos usar o console do Lightsail, então use esse endereço: &lt;code&gt;https://lightsail.aws.amazon.com/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Aqui poderemos escolher a opções para criação da nossa instância. Não irei mudar a região da instancia. Em seguida vamos escolher a plataforma &lt;strong&gt;Linux/Unix&lt;/strong&gt; e o &lt;em&gt;&lt;strong&gt;blueprint&lt;/strong&gt;&lt;/em&gt; vamos escolher &lt;strong&gt;Os Only&lt;/strong&gt; e escolher CentOS 7 (o CentOS 8 já perdeu o suporte, enquanto o 7 vai até 2024).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmfld7f7o33yoxohsqnl9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmfld7f7o33yoxohsqnl9.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mais abaixo na página, vamos escolher a opção com 2gb de memória RAM de acordo com os &lt;a href="https://docs.couchbase.com/server/current/install/pre-install.html" rel="noopener noreferrer"&gt;requisitos mínimos do Couchbase Server&lt;/a&gt;. Em seguida vamos escolher um nome para a instância.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9to3tuwsfh13arehqn6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy9to3tuwsfh13arehqn6.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depois disso basta clicar no botão &lt;strong&gt;Create instance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;O console irá mostrar a instância como pendente por um tempo, basta aguardar:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0f3j3f6s78f0x91t6o22.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0f3j3f6s78f0x91t6o22.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depois que ela estiver rodando, precisamos acessar ela via ssh. Para isso acesse o menu Account no canto superior direito. Na tela seguinte acesse a aba &lt;strong&gt;SSH Keys&lt;/strong&gt;. Faça o download da chave pelo link conforme imagem a seguir:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwd1ixohrh7lk7dd0ktgu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwd1ixohrh7lk7dd0ktgu.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Antes de podermos usar essa chave, vamos precisar converter ela para o formato suportado pelo putty. Abra o puttygen e escolha o botão &lt;strong&gt;Load&lt;/strong&gt; e escolha o arquivo .PEM que baixamos:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmyil662f5odarx8kp8r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmyil662f5odarx8kp8r.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora clique em &lt;strong&gt;Save private key&lt;/strong&gt;. Ele vai perguntar se deseja salvar sem definir uma senha. Eu decidi seguir sem senha e depois escohi um lugar para o arquivo.&lt;/p&gt;

&lt;p&gt;Agora podemos usar essa chave para acessar o servidor via putty. Insira o arquivo na seguinte configuração:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Florp0099evpsxqs0qvaz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Florp0099evpsxqs0qvaz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depois disso vamos voltar na primeira sessão e preencher o IP do servidor para podermos fazer o acesso:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgj3fnm3m9ryfxq5yf8wy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgj3fnm3m9ryfxq5yf8wy.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ao fazer o acesso será solicitado o usuário, que no padrão para essa imagem de sistema que escolher é &lt;strong&gt;&lt;em&gt;centos&lt;/em&gt;&lt;/strong&gt;. E pronto, temos acesso ao shell do nosso servidor:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3308l1sio6pxc8qcopog.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3308l1sio6pxc8qcopog.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Antes de seguirmos, vamos criar um endereço de IP fixo para essa instância, senão toda vez que reiniciarmos o servidor ele vai iniciar com um IP diferente. &lt;/p&gt;

&lt;p&gt;Acesse a aba &lt;strong&gt;Networking&lt;/strong&gt; e escolha a opção &lt;strong&gt;Create static IP&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgd8mohjepgex5xswj8qw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgd8mohjepgex5xswj8qw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Escolha a instancia que criamos, defina o um nome para o IP stático e clique em &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxdojvnmm7yh168zm9gra.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxdojvnmm7yh168zm9gra.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depois da criação será exibido o novo IP estático que iremos usar agora no nosso putty de forma permanente. &lt;/p&gt;

&lt;p&gt;Agora que já estamos com nossa instância no ar, vamos partir para a instalação e configuração do servidor Couchbase e do Gateway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Instalando o Couchbase Server&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Vamos começar atualizando os pacotes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;yum update &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seguindo as &lt;a href="https://docs.couchbase.com/server/current/install/rhel-suse-install-intro.html" rel="noopener noreferrer"&gt;instruções da documentação&lt;/a&gt; vamos digitar os comandos abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://packages.couchbase.com/releases/couchbase-release/couchbase-release-1.0-x86_64.rpm

&lt;span class="nb"&gt;sudo &lt;/span&gt;rpm &lt;span class="nt"&gt;-i&lt;/span&gt; ./couchbase-release-1.0-x86_64.rpm

&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;couchbase-server-community &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vamos abrir as portas necessárias no firewall da AWS:&lt;/p&gt;

&lt;p&gt;Use a opção Manage no console para acessar as configurações da instância:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd70hqxuf2s21xpzty0g3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd70hqxuf2s21xpzty0g3.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora na abra &lt;strong&gt;Networking&lt;/strong&gt; e use o botão &lt;strong&gt;Add rule&lt;/strong&gt; para incluir as portas conforme imagem abaixo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahqjyzazugdu5mq95r3i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahqjyzazugdu5mq95r3i.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Com isso será possível acessar o console do Couchbase Server:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31x909dx8gacuq5zcitc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F31x909dx8gacuq5zcitc.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A partir daqui o passo a passo já foi abordado no artigo anterior dessa série, então vou passar agora para a instalação do Couchbase Gateway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Instalando o Couchbase Gateway&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Vamos precisar baixar um pacote, então vamos instalar o wget primeiro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;wget &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora seguindo o passo-a-passo da &lt;a href="https://docs.couchbase.com/sync-gateway/3.0/get-started-install.html" rel="noopener noreferrer"&gt;documentação&lt;/a&gt; vamos baixar o pacote com o comando abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget http://packages.couchbase.com/releases/couchbase-sync-gateway/3.0.0-beta02/couchbase-sync-gateway-community_3.0.0-beta02_x86_64.rpm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E agora a instalação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;rpm &lt;span class="nt"&gt;-i&lt;/span&gt; couchbase-sync-gateway-community_3.0.0-beta02_x86_64.rpm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado é o gateway já rodando na porta 4984. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy8wpyhnv8tvoycren6zp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy8wpyhnv8tvoycren6zp.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos adicionar essas portas também ao firewall:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11chp274ch17zqktcfg3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F11chp274ch17zqktcfg3.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Assim como no artigo anterior, nesse ponto você precisa conseguir acessar o gateway e obter o retorno da API. Se não conseguir obter esse retorno, pare e revise os passos anteriores:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F51zzct3f5kqf1tcf86rc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F51zzct3f5kqf1tcf86rc.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos acessar as configurações do gateway e apontar ele para nosso servidor couchbase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /home/sync_gateway/sync_gateway.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Vamos alterar da mesma forma que fizemos no artigo anterior:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frwjbpn97nv73v7424rhc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frwjbpn97nv73v7424rhc.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"adminInterface"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"127.0.0.1:4985"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"interface"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0:4984"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"databases"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"my-database"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:8091"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bucket"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-database"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sync_gateway"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"******"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"enable_shared_bucket_access"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"import_docs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"num_index_replicas"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"GUEST"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"disabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"admin_channels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"logging"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"console"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"log_level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"debug"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"log_keys"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Denovo... você precisa conseguir acessar a API após essa alteração antes de continuar.&lt;/p&gt;

&lt;p&gt;Agora vamos executar o mesmo teste que fizemos no artigo anterior via poweshell para confirmar que tudo está funcionando corretamente:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftktcs8gnqnfpkt8f951s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftktcs8gnqnfpkt8f951s.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;E acessando o Couchbase Server devemos ver o registro que acabamos de inserir:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxr3z0bvtz9qa2e2pzek7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxr3z0bvtz9qa2e2pzek7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;É isso aí! Temos um servidor na nuvem pronto para receber as requisições do nosso aplicativo.&lt;/p&gt;

&lt;p&gt;No próximo artigo dessa série, vamos falar um pouco sobre segurança.&lt;/p&gt;

&lt;p&gt;Nos vemos lá.&lt;/p&gt;

</description>
      <category>couchbase</category>
      <category>flutter</category>
      <category>aws</category>
      <category>lightsail</category>
    </item>
    <item>
      <title>Eu tenho um sonho ... offline-first em Flutter</title>
      <dc:creator>Filipe Nanclarez</dc:creator>
      <pubDate>Sat, 26 Feb 2022 19:10:19 +0000</pubDate>
      <link>https://dev.to/filipenanclarez/eu-tenho-um-sonho-offline-first-em-flutter-4nf8</link>
      <guid>https://dev.to/filipenanclarez/eu-tenho-um-sonho-offline-first-em-flutter-4nf8</guid>
      <description>&lt;p&gt;Quando pensamos em desenvolvimento de aplicativos, logo nos lembramos dos problemas de conexão que todos os usuários enfrentam. E como desenvolvedor com certeza você já deve ter ouvido a pergunta de um milhão: "como fazer &lt;strong&gt;offline-first&lt;/strong&gt;?". Sempre que fiz essa pergunta a resposta foi "isso é complicado". Ao mesmo tempo sempre ouço "tem o firebase, mas ...". &lt;/p&gt;

&lt;p&gt;De fato a solução da Google é muito atraente. Principalmente para quem está começando e não quer gastar muita energia com a parte de backend, servidores, configuração de clusters, etc.&lt;/p&gt;

&lt;p&gt;Mas ele tem suas limitações, e essas limitações acabam levando os desenvolvedores a buscar outras soluções cedo ou tarde, principalmente quando esbarram em algum problema que ele não pode resolver. &lt;/p&gt;

&lt;p&gt;No meu caso, eu até poderia aceitar a ideia de pagar um pouco mais pelo serviço, afinal era só uma questão matemática, o quanto eu iria cobrar dos meus usuários seria equivalente ao uso. Quanto mais usuários, mais receita e o custo ia crescer de acordo. Mas o que me fez ir atrás de outra solução foi uma limitação técnica. O limite do tamanho do registro do Firestore. Conforme a &lt;a href="https://firebase.google.com/docs/firestore/quotas" rel="noopener noreferrer"&gt;documentação&lt;/a&gt; um documento não pode ter mais de 1 MiB. Meu aplicativo precisa de mais que isso. Tentei quebrar o conteúdo, mas isso me trouxe mais problemas do que soluções.&lt;/p&gt;

&lt;p&gt;Acabei focando em outras partes do aplicativo que precisavam ficar prontas, e deixei esse assunto na gaveta. Até que um dia me deparei com o artigo &lt;a href="https://medium.com/flutter-community/how-to-make-your-flutter-app-offline-first-with-couchbase-lite-86bb23780f74" rel="noopener noreferrer"&gt;How to make your Flutter app offline-first with Couchbase Lite&lt;/a&gt;. Isso me chamou bastante atenção e após ler ele superficialmente deixei ele guardado na gaveta.&lt;/p&gt;

&lt;p&gt;Algum tempo depois meu aplicativo já estava publicado, os usuários estavam contentes, e o problema que eu havia conscientemente jogado para o canto da mesa, agora precisava ser resolvido.&lt;/p&gt;

&lt;p&gt;Então arregacei as mangas e comecei a trilhar esse caminho. &lt;/p&gt;

&lt;p&gt;O que vem a seguir é baseado nesse artigo do &lt;a href="https://medium.com/@gabriel.terwesten" rel="noopener noreferrer"&gt;Gabriel Terwesten&lt;/a&gt; e na documentação do próprio site da Couchbase. Algumas imagens foram tiradas dessas fontes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Os pacotes usados nesse artigo até o momento &lt;strong&gt;não dão suporte para web&lt;/strong&gt;, então se você tem projetos web não vai conseguir, por enquanto, usar essa solução. 😉&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Índice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;O aplicativo&lt;/li&gt;
&lt;li&gt;O servidor&lt;/li&gt;
&lt;li&gt;O gateway&lt;/li&gt;
&lt;li&gt;Conferindo a comunicação&lt;/li&gt;
&lt;li&gt;O replicador&lt;/li&gt;
&lt;li&gt;Conclusão&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mas o que é essa tal de Couchbase?&lt;/p&gt;

&lt;p&gt;A Couchbase fornece uma solução de banco de dados bem robusta e com características interessantes. A solução é de código aberto, e possui uma versão gratuita mantida pela comunidade.&lt;/p&gt;

&lt;p&gt;No caso do flutter, o autor do artigo mencionado acima explica porque ele criou os packages que vamos usar abaixo.&lt;/p&gt;

&lt;p&gt;Ao procurar esses packages no pub.dev você talvez fique confuso porque são vários packages separados. Segundo o artigo, basicamente o pacote com que nós iremos interagir é o &lt;code&gt;cbl_flutter&lt;/code&gt; ou o &lt;code&gt;cbl_dart&lt;/code&gt;. Se você como eu está se perguntado se deve usar o &lt;code&gt;cbl_dart&lt;/code&gt; porque seu aplicativo precisa rodar em qualquer plataforma, fique tranquilo, o &lt;code&gt;cbl_flutter&lt;/code&gt; também roda.&lt;/p&gt;

&lt;p&gt;Esse pacote depende do clb, que depende do &lt;code&gt;cbl_ffi&lt;/code&gt; que depende de outros e assim por diante. Para facilitar vamos nos concentrar apenas nas dependências necessárias e fazer uma POC simples pra ver o negócio funcionando.&lt;/p&gt;

&lt;h3&gt;
  
  
  O aplicativo...&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Nosso foco é o couchlite, então não vamos perder tempo com layout nem perfumaria. Vamos fazer o seguinte, vamos pegar o projeto padrão do flutter, e na rotina que incrementa o contador, vamos fazer o aplicativo gravar uma informação no banco. Simples assim! Com isso vamos entender o funcionamento e arquitetura da solução da Couchbase sem gastar energia com detalhes que apenas ofuscariam nossa visão do conceito principal.&lt;/p&gt;

&lt;p&gt;Então vamos lá.&lt;/p&gt;

&lt;p&gt;Crie uma pasta com o nome que quiser, e estando dentro dela abra seu prompt e inicie o projeto padrão do flutter.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;flutter create .&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Isso vai criar aquele projeto que todos nós conhecemos do contador:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fytii7q8v1iq4fitnc5f4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fytii7q8v1iq4fitnc5f4.png" alt="Projeto padrão flutter em windows"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos adicionar as dependências no pubspec.yaml. Se você for no &lt;a href="https://pub.dev" rel="noopener noreferrer"&gt;pub.dev&lt;/a&gt; procurar o package cbl, e for ler o readme, vai ficar surpreso com a parte da instalação:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhie3tzdrl15fmbpujrvw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhie3tzdrl15fmbpujrvw.png" alt="pubspec.yaml do package cbl"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Legal, eles não colocaram as versões. Mas isso nunca foi um problema certo, basta eu usar o método  &lt;code&gt;flutter pub add&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Então vamos fazer isso para todos os pacotes que precisamos. Você já vai entender porque estou detalhando um passo tão óbvio.&lt;/p&gt;

&lt;p&gt;De cima pra baixo, vamos instalar:&lt;/p&gt;

&lt;p&gt;O pacote principal da biblioteca:&lt;br&gt;
&lt;code&gt;flutter pub add cbl&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Depois o pacote do clb_flutter&lt;br&gt;
&lt;code&gt;flutter pub add cbl_flutter&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;E por último o pacote especifico do flutter, na versão comunity do couchlite:&lt;br&gt;
&lt;code&gt;flutter pub add cbl_flutter_ce&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;E aí somos surpreendidos com esse erro curioso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Because cbl_flutter_platform_interface &amp;gt;=1.0.0-beta.1 &amp;lt;1.0.0-beta.2 depends on cbl ^1.0.0-beta.7 and cbl_flutter_platform_interface &amp;lt;1.0.0-beta.1 depends on cbl ^1.0.0-beta.6, cbl_flutter_platform_interface &amp;lt;1.0.0-beta.2 requires cbl ^1.0.0-beta.6.
And because cbl_flutter_ce &amp;lt;1.0.0-beta.2 depends on cbl_flutter_platform_interface ^1.0.0-beta.0 and cbl_flutter_ce &amp;gt;=1.0.0-beta.2 &amp;lt;1.0.0-beta.3 depends on cbl_flutter_platform_interface ^1.0.0-beta.2, cbl_flutter_ce &amp;lt;1.0.0-beta.3 requires cbl ^1.0.0-beta.6 or cbl_flutter_platform_interface ^1.0.0-beta.2.
And because cbl_flutter_platform_interface &amp;gt;=1.0.0-beta.2 &amp;lt;1.0.0-beta.3 depends on cbl ^1.0.0-beta.8 and cbl_flutter_ce &amp;gt;=1.0.0-beta.3 &amp;lt;1.0.0-beta.4 depends on cbl_flutter_platform_interface ^1.0.0-beta.3, cbl_flutter_ce &amp;lt;1.0.0-beta.4 requires cbl ^1.0.0-beta.6 or cbl_flutter_platform_interface ^1.0.0-beta.3.
And because cbl_flutter_platform_interface &amp;gt;=1.0.0-beta.3 &amp;lt;1.0.0-beta.4 depends on cbl ^1.0.0-beta.10 and cbl_flutter_ce &amp;gt;=1.0.0-beta.4 &amp;lt;1.0.0-beta.6 depends on cbl_flutter_platform_interface ^1.0.0-beta.4, cbl_flutter_ce &amp;lt;1.0.0-beta.6 requires cbl ^1.0.0-beta.6 or cbl_flutter_platform_interface ^1.0.0-beta.4.
Because cbl_flutter_platform_interface &amp;gt;=1.0.0-beta.5 &amp;lt;1.0.0-beta.6 depends on cbl ^1.0.0-beta.13 and cbl_flutter_platform_interface &amp;gt;=1.0.0-beta.4 &amp;lt;1.0.0-beta.5 depends on cbl ^1.0.0-beta.11, cbl_flutter_platform_interface &amp;gt;=1.0.0-beta.4 &amp;lt;1.0.0-beta.6 requires cbl ^1.0.0-beta.11.
Thus, cbl_flutter_ce &amp;lt;1.0.0-beta.6 requires cbl ^1.0.0-beta.6 or cbl_flutter_platform_interface ^1.0.0-beta.6.
And because cbl_flutter_ce &amp;gt;=1.0.0-beta.6 depends on cbl_flutter_platform_interface ^1.0.0-beta.6 which depends on cbl ^1.0.0-beta.15, every version of cbl_flutter_ce requires cbl ^1.0.0-beta.6.
So, because poc_flutter_couchbase depends on both cbl ^0.6.0+1 and cbl_flutter_ce any, version solving failed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O erro já explica o que está acontecendo. As versões do pub.dev não estão sincronizadas corretamente.&lt;/p&gt;

&lt;p&gt;Pra conseguir pegar a versão correta, basta clicar na versão 'Prerelease' lá no pub.dev. Com isso será possível ver as versões que ainda estão em desenvolvimento&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq2f10qouhasoiutw7pm0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq2f10qouhasoiutw7pm0.png" alt="link pre-release no pub.dev"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Se você não quer perder tempo com isso, basta colocar as seguintes versões no seu pubspec.yaml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="nl"&gt;cbl:&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;
  &lt;span class="nl"&gt;cbl_flutter:&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;
  &lt;span class="nl"&gt;cbl_flutter_ce:&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;beta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vamos ao código. Você pode vasculhar a documentação no site da Couchbase e também dos pacotes para ver exemplos de como fazer um CRUD simples. &lt;/p&gt;

&lt;p&gt;No nosso caso, vamos manter o foco em inserir alguma coisa no banco. Então primeiro precisamos iniciar o banco. Para isso vamos adicionar no método iniState um 'PostFrameCallback' com a chamada para iniciar e abrir o banco. Se você é novo em flutter, estamos colocando aqui, para que essa inicialização aconteça apenas uma vez.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// TODO: implement initState&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;WidgetsBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;addPostFrameCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;timeStamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;CouchbaseLiteFlutter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'database-example'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depois de iniciar o banco, agora podemos usar ele em nossa aplicação.&lt;/p&gt;

&lt;p&gt;Na função &lt;code&gt;_incrementCounter()&lt;/code&gt; vamos inserir os comandos para inclusão de um registro no banco. Basicamente criamos um documento e depois usamos o método &lt;code&gt;saveDocument()&lt;/code&gt; pra salvar ele no banco:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_incrementCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MutableDocument&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="s"&gt;'type'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'logMessage'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;'createdAt'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="s"&gt;'message'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'teste'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;saveDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora para podermos ver o registro que foi inserido, vamos precisar consultar o banco e trazer os resultados. &lt;/p&gt;

&lt;p&gt;Então vamos fazer um select simples, e imprimir isso no log. Mude novamente a função para isso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;    &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_incrementCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MutableDocument&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="s"&gt;'type'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'logMessage'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;'createdAt'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="s"&gt;'message'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'teste'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;saveDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// consultando dados no banco&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;QueryBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;SelectResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;expression&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="n"&gt;SelectResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'createdAt'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="n"&gt;SelectResult&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DataSource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'type'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equalTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'logMessage'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Ordering&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'createdAt'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="n"&gt;ResultSet&lt;/span&gt; &lt;span class="n"&gt;resultSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;resultSet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asStream&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toPlainMap&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;'createdAt'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;replicator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute o aplicativo, aperte o botão de incrementar e se tudo der certo você verá os dados que acabou de inserir no console assim:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpo663xztvh8goaxbs1p1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpo663xztvh8goaxbs1p1.png" alt="console exibindo os dados inseridos"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Com isso já temos um aplicativo gravando dados e exibindo esses dados. Uma POC bem simples, pra mostrar como funciona a parte 'lite' do Couchbase.&lt;/p&gt;

&lt;p&gt;Vamos agora dar um passo a mais. &lt;/p&gt;

&lt;h3&gt;
  
  
  O servidor...&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Nós começamos esse artigo falando sobre a necessidade do &lt;strong&gt;offline-first&lt;/strong&gt; e do Firestore. Agora nós criamos uma POC gravando dados num app. Mas isso está mais parecido com &lt;strong&gt;Sqlite&lt;/strong&gt; do que com Firestore certo? Certo!&lt;/p&gt;

&lt;p&gt;Para que tudo isso fique mais parecido com o Firestore, precisamos de um &lt;strong&gt;servidor&lt;/strong&gt;, e precisamos que o aplicativo e o servidor &lt;strong&gt;sincronizem&lt;/strong&gt; os dados de forma transparente para o usuário ( se também for transparente para os desenvolvedores... melhor ainda ).&lt;/p&gt;

&lt;p&gt;Então vamos lá, vamos instalar um servidor de banco de dados. Vamos continuar mantendo as coisas simples e diretas, mas para que você entenda a proposta da solução, a ideia é que no final você possa contratar uma VM na nuvem, e você tenha seu próprio servidor. Por exemplo você poder usar o serviço &lt;a href="https://aws.amazon.com/pt/lightsail/" rel="noopener noreferrer"&gt;Lightsail&lt;/a&gt; da AWS ou ainda usar o &lt;a href="https://aws.amazon.com/pt/ec2/?did=ft_card&amp;amp;trk=ft_card" rel="noopener noreferrer"&gt;EC2&lt;/a&gt; e ter um servidor gratuito por um ano. Ou usar outros provedores para ter seu próprio servidor na nuvem. Depois você instala o Couchserver nesse servidor, e com isso você terá uma solução para usar no lugar do Firestore, com a vantagem de não ter as limitações que já mencionamos.&lt;/p&gt;

&lt;p&gt;Então vamos lá. Vamos instalar o servidor no nosso computador local mesmo, e fazer isso tudo funcionar. Depois pensamos em como colocar isso em um servidor na nuvem.&lt;/p&gt;

&lt;p&gt;Para baixar o Couchbase server acesse a página oficial &lt;a href="https://www.couchbase.com/downloads" rel="noopener noreferrer"&gt;nesse link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Em Couchbase Server escolha a versão community, no meu caso estou usando essa aqui:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flafm3ggl2o28dntyznn1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flafm3ggl2o28dntyznn1.png" alt="página de downloads do site couchbase.com"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depois da instalação, seguindo a documentação em &lt;a href="https://docs.couchbase.com/server/current/manage/manage-nodes/create-cluster.html" rel="noopener noreferrer"&gt;Create a Cluster&lt;/a&gt; devemos acessar o console do Couchbase Server pelo navegador na porta padrão 8091. Vamos cair nessa página:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7m8hosqv03xokz5rzif4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7m8hosqv03xokz5rzif4.png" alt="pagina inicial do couchbase"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos escolher a opção "Setup New Cluster"&lt;/p&gt;

&lt;p&gt;O nome do cluster fica a seu critério. Deixei o meu setup da seguinte forma:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mg4zh6oit5ltljwikds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9mg4zh6oit5ltljwikds.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Após isso verá a tela dos termos. Após as opções de aceitar os termos, há duas opções; terminar com os padrões ou configurar disco memória e serviços.&lt;/p&gt;

&lt;p&gt;Para fins didáticos, vamos escolher a opção 'Configure Disk, Memory, Services'.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzz1mk6ctam0rw7tv3dy9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzz1mk6ctam0rw7tv3dy9.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nessa tela, podemos ver as opções disponíveis. Eu não vou mudar nada no meu setup, mas achei bom trazer vocês até aqui para vocês conhecerem as opções disponíveis.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl8bssnzw72wh7fg5j77t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl8bssnzw72wh7fg5j77t.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Após clicar em finalizar, seremos redirecionados para o Dashboard principal desse nó, conforme imagem abaixo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0n2efn7mbpwtwfivdw0o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0n2efn7mbpwtwfivdw0o.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos precisar criar nosso bucket, para armazenar nossos dados. Clique em bucket no menu de opções:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkg9o11om9ewib0fp0u2p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkg9o11om9ewib0fp0u2p.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depois clique em 'ADD BUCKET'&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6busiz2ckuwyfdm6oyq6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6busiz2ckuwyfdm6oyq6.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Aqui vou apenas escolher o nome. No print abaixo, deixei aberta as opções avançadas apenas para fins didáticos:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3bmjopokrm5qa464yl7m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3bmjopokrm5qa464yl7m.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  O gateway...&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;A comunicação com o servidor não é direta. É necessário um intermediário entre uma instancia local do CouchLite (seu aplicativo) e o Servidor Couchbase.&lt;/p&gt;

&lt;p&gt;Para entender melhor o fluxo, veja essa imagem tirada do site da couchbase:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdo3te60nz8kitvdyt8a9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdo3te60nz8kitvdyt8a9.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ou seja, nosso aplicativo fala como gateway, e o gateway fala com o servidor. Então &lt;strong&gt;bora instalar esse cara!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A instalação segue o padrão NNF (Next, Next, Finish).&lt;/p&gt;

&lt;p&gt;Após finalizar, vamos ter as informações sobre a porta em que o gateway estará rodando. Anote isso!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu95tofwsbg9hd3qzduup.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu95tofwsbg9hd3qzduup.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos acessar esse endereço que anotamos.&lt;/p&gt;

&lt;p&gt;Deve aparecer isso na tela:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8i2kl1vuljhh2ofiy8k7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8i2kl1vuljhh2ofiy8k7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Isso significa que deu certo a instalação. Caso não tenha aparecido esse retorno da API pare aqui e repasse a instalação antes de seguir.&lt;/p&gt;

&lt;p&gt;Agora precisamos configurar o Gateway para acessar o servidor do CouchBase. Primeiro vamos criar um usuário para que o gateway possa acessar o servidor.&lt;/p&gt;

&lt;p&gt;Acesse Security e depois o botão 'ADD USER' conforme imagem abaixo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvcvr5mcvl03y6qn870p4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvcvr5mcvl03y6qn870p4.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Preencha o nome, e a senha, e nas permissões deixe da seguinte forma:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F218doxpo7nem3z7ja3t2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F218doxpo7nem3z7ja3t2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos parar serviço do gateway lá em services.msc (no windows). Depois que o serviço estiver parado vamos editar o arquivo de configurações do gateway. Na minha instalação está em &lt;code&gt;c:\Program Files\Couchbase\Sync Gateway\serviceconfig.json&lt;/code&gt;. Após achar o arquivo edite ele e altere o json pra ficar parecido com isso (preencha com as suas informações é claro, &lt;strong&gt;principalmente a senha&lt;/strong&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"adminInterface"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"127.0.0.1:4985"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"interface"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0:4984"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"databases"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"my-database"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://127.0.0.1:8091"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bucket"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-database"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="nl"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sync_gateway"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"******"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"enable_shared_bucket_access"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="nl"&gt;"import_docs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="nl"&gt;"num_index_replicas"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="nl"&gt;"users"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"GUEST"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"disabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"admin_channels"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"logging"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="nl"&gt;"console"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"log_level"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"debug"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"log_keys"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Depois disso, inicie novamente o serviço. Você deve conseguir ver o retorno da API novamente ao tentar acessar o gateway pelo navegador em localhost/4985. Se não conseguir, pare e reveja passo a passo. Uma dica importante. Eu perdi um tempo considerável aqui, por conta de porta travada no computador. Se você reviu o passo a passo, e ainda não consegue acessar, sugiro que reinicie o computador. No meu caso foi o suficiente para o gateway responder corretamente após os ajustes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conferindo a comunicação&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Para termos certeza de que tudo está ok, vamos inserir dados no servidor via API do gateway. Poderíamos fazer essa requisição de várias formas, usando postman por exemplo. Mas para facilitar vou usar a requisição com o curl, via powershell com o cmdlet &lt;a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-webrequest?view=powershell-7.2" rel="noopener noreferrer"&gt;Invoke-WebRequest&lt;/a&gt;. Abra uma janela do powershell e cole o comando abaixo (lembre de trocar o nome do bucket na url):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iwr http://localhost:4984/my-database/ &lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nt"&gt;-Method&lt;/span&gt; &lt;span class="s1"&gt;'POST'&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nt"&gt;-ContentType&lt;/span&gt; &lt;span class="s1"&gt;'application/json; charset=utf-8'&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="nt"&gt;-Body&lt;/span&gt; &lt;span class="s1"&gt;'{ 
  "_id": "first-doc", 
  "name": "Hello Word Couchbase", 
  "type": "teste-doc", 
  "data": "Hello Word" 
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Devemos obter esse retorno:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0mupkxb1p9js92s13i3i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0mupkxb1p9js92s13i3i.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora vamos no couchserver, verificar o documento criado:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7v29haobqhbs11b0x3z7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7v29haobqhbs11b0x3z7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F21pzkoas49a8uxidfru1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F21pzkoas49a8uxidfru1.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Olha ele aí:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9goh6qkievxus4hia26.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9goh6qkievxus4hia26.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Com isso sabemos que o gateway está ok. Agora vamos voltar ao flutter.&lt;/p&gt;

&lt;h3&gt;
  
  
  O replicador...&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Vamos adicionar no PostFrameCallback um &lt;strong&gt;replicador&lt;/strong&gt;. Esse cara será chamado toda vez que quisermos sincronizar alguma coisa com o servidor. Pode ser após inserir uma informação, ou quando o aplicativo iniciar, ou quando ele voltar a ter conexão com a internet, enfim, você que escolhe em que momento ele deverá fazer isso. No momento da sincronização ele envia e recebe os dados do servidor (via gateway) e em caso de conflito (se o registro foi editado tanto no servidor quanto na aplicação antes da sincronização) a resolução de conflitos padrão escolhe como vencedor o último registro que foi gravado.&lt;/p&gt;

&lt;p&gt;Abaixo o código modificado do método &lt;code&gt;iniState()&lt;/code&gt;. Repare que também adicionamos um listener para printar o status do replicador. Você pode por exemplo usar isso para exibir algum loader no aplicativo. Aqui estamos apenas imprimindo no console as mudanças de estado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="nd"&gt;@override&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// TODO: implement initState&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;WidgetsBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;addPostFrameCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;timeStamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;CouchbaseLiteFlutter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'example.couchbase'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// replicador&lt;/span&gt;
      &lt;span class="n"&gt;replicator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Replicator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;ReplicatorConfiguration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nl"&gt;database:&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nl"&gt;target:&lt;/span&gt; &lt;span class="n"&gt;UrlEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Uri&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'ws://localhost:4984/my-database'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;replicator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addChangeListener&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;change&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;debugPrint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Replicator activity: &lt;/span&gt;&lt;span class="si"&gt;${change.status.activity}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;esse código só cria o 'replicator' mas não dispara uma sincronização. Então vamos adicionar uma sincronização logo após o usuário apertar o botão para incrementar o contador. Assim o sistema irá adicionar um registro e logo em seguida irá disparar uma sincronização:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_incrementCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MutableDocument&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="s"&gt;'type'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'logMessage'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;'createdAt'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="s"&gt;'message'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'teste'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="na"&gt;saveDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;replicator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;_counter&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Faça o teste, e se tudo der certo, você verá no console do couchserver os documentos. Se você for como eu, talvez dê alguns pulos de alegria também.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4t25z6w6mx5tk5912f9m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4t25z6w6mx5tk5912f9m.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Você pode fazer alguns testes parando o serviço do Couchbase Server, gravando dados no aplicativo, depois iniciando o serviço denovo e incluindo mais dados e depois verificando se tanto os dados gerados enquanto você estava com o servidor parado quanto os dados gerados após o servidor estar novamente em execução foram gravados corretamente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusão&lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;O que fizemos até aqui já deve ter dado uma bela clareada na sua mente sobre como isso tudo funciona. Mas é claro que isso é uma POC bem simples, e com certeza precisamos fazer mais coisas, como exibir os dados de maneira mais elegante, filtrar os dados a serem sincronizados (afinal não queremos dados de todos os outros usuários que usam o aplicativo sobrecarregando nosso celular), criar alguma forma de autenticar em nosso gateway para evitar que qualquer um possa inserir dados via powershell como fizemos, sem falar na parte de configurar um servidor na nuvem para que a coisa fique mais parecida com a realidade.&lt;/p&gt;

&lt;p&gt;Ainda temos um bom caminho pela frente.&lt;/p&gt;

&lt;p&gt;No próximo artigo vamos dar uma olhada em tudo isso. &lt;/p&gt;

</description>
      <category>offlinefirst</category>
      <category>couchbase</category>
      <category>flutter</category>
      <category>firestore</category>
    </item>
  </channel>
</rss>
