O poder dos WebViews em Flutter
Tags: flutter, dart
Published: 2019-07-20 21:59:04

---
tags: flutter, dart
title: O poder dos WebViews em Flutter
description: Primeiro artigo da série onde irei traduzir artigos sobre Flutter escritos originalmente em outros idiomas, para mais acessibilidade e…
---


<p name="de2b" id="de2b" class="graf graf--p graf-after--h3 graf--trailing">Primeiro artigo da série onde irei traduzir artigos sobre Flutter escritos originalmente em outros idiomas, para mais acessibilidade e suporte na comunidade de língua portuguesa. Artigo originalmente publicado por <a href="https://medium.com/u/45d6b1e163cc" data-href="https://medium.com/u/45d6b1e163cc" data-anchor-type="2" data-user-id="45d6b1e163cc" data-action-value="45d6b1e163cc" data-action="show-user-card" data-action-type="hover" class="markup--user markup--p-user" target="_blank">Emily Fortuna</a> que nos falar sobre webViews e a importância do uso de chaves (Key).</p></div></div></section><section name="7cce" class="section section--body"><div class="section-divider"><hr class="section-divider"></div><div class="section-content"><div class="section-inner sectionLayout--insetColumn"><p name="81bb" id="81bb" class="graf graf--p graf--leading">Você quer escrever uma aplicação que pode mostrar uma página web sem ter que abrir o navegador do seu dispositivo móvel? Ou talvez você já tem um fluxo de pagamento seguro implementado em seu site e não querem voltar a implementar a funcionalidade de pagamento na sua aplicação móvel — o dinheiro pode ser um negócio complicado e não quer acabar acidentalmente enviando metade do pagamento ao Fundo salvou o Kraken. É só eu? Bem, de qualquer forma, a equipe de Flutter criou um <a href="https://pub.dartlang.org/packages/webview_flutter" data-href="https://pub.dartlang.org/packages/webview_flutter" class="markup--anchor markup--p-anchor" rel="noopener" target="_blank">plugin muito legal</a> que permite que você incorporar em sua aplicação WebViews para tornar esta funcionalidade possível.</p><p name="eda2" id="eda2" class="graf graf--p graf-after--p">Me referia a funcionalidade de mostrar páginas web em aplicações Flutter…não reabilitando krakens.</p><h3 name="e695" id="e695" class="graf graf--h3 graf-after--p">WebViews Flutter são como qualquer outro widget</h3><p name="7b1c" id="7b1c" class="graf graf--p graf-after--h3">Incorporar o plugin do WebView no seu aplicativo é extremamente simples. É apenas um widget como qualquer outro: <code class="markup--code markup--p-code">WebView(initialUrl: ‘https://flutter.io&#39;)</code>. Você pode também activar ou desactivar o JavaScript no seu WebView com o parámetro <code class="markup--code markup--p-code">javascriptMode</code>. Por padrão, JavaScript é desactivado, para ativar ele você construiria um WebView como este:</p><pre name="0734" id="0734" class="graf graf--pre graf-after--p">WebView(<br>  initialUrl: &#39;https://flutter.io&#39;,<br>  javascriptMode: JavascriptMode.unrestricted,<br>)</pre><p name="da8f" id="da8f" class="graf graf--p graf-after--pre">Praticamente todas as informações que você quer saber sobre o seu WebView e também a capacidade de controlar o seu WebView acontecem através do… espera… WebViewController. Este controlador é retornado em um retorno de chamada quando o WebView é totalmente construído:</p><pre name="c300" id="c300" class="graf graf--pre graf-after--p">WebViewController _controller;<br>WebView(<br>  initialUrl: &#39;https://flutter.io&#39;,<br>  onWebViewCreated: (WebViewController webViewController) {<br>    _controller = webViewController;<br>  },<br>);</pre><pre name="6f68" id="6f68" class="graf graf--pre graf-after--pre">//...later on, probably in response to some event:<br>_controller.loadUrl(&#39;http://dartlang.org/&#39;<br>);</pre><p name="2a28" id="2a28" class="graf graf--p graf-after--pre">O WebViewController é seu tíquete para modificar o WebView programaticamente pelo Flutter ou acessar propriedades como o URL atual que está sendo exibido. Para ver como tudo isso funciona na prática, eu escrevi um aplicativo simples de navegação na Wikipédia que permite que você marque as páginas para mais tarde, para que o teórico nunca esqueça o último artigo fascinante da Wikipedia na próxima vez em que você mesmo caindo em um <a href="https://en.wikipedia.org/wiki/Wiki_rabbit_hole" data-href="https://en.wikipedia.org/wiki/Wiki_rabbit_hole" class="markup--anchor markup--p-anchor" rel="noopener" target="_blank">Wiki do buraco de coelho</a>.</p><figure name="e5ab" id="e5ab" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 320px; max-height: 636px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 198.8%;"></div><img class="graf-image" data-image-id="1*FzIWOggb4M2K84SnMlPAyQ.gif" data-width="320" data-height="636" data-is-featured="true" src="https://cdn-images-1.medium.com/max/800/1*FzIWOggb4M2K84SnMlPAyQ.gif"></div><figcaption class="imageCaption">A Wikipedia-exploring app written in Flutter using WebViews. You can favorite pages and save them for later viewing</figcaption></figure><p name="952b" id="952b" class="graf graf--p graf-after--figure">O código completo deste navegador do Wiki-do-buraco-de-coelho pode ser encontrado neste <a href="https://github.com/efortuna/wiki_browser" data-href="https://github.com/efortuna/wiki_browser" class="markup--anchor markup--p-anchor" rel="noopener" target="_blank">repositório GitHub</a>.</p><figure name="1219" id="1219" class="graf graf--figure graf--iframe graf-after--p"><script src="https://gist.github.com/efortuna/a8c30915ae9c8950ec3a85541102095f.js.js"></script></figure><p name="bf1f" id="bf1f" class="graf graf--p graf-after--figure">WebView é apenas como qualquer outro widget em Flutter e pode ser composto com outros widgets em camadas sobre ele. Note que o botão de favorito é apenas um <code class="markup--code markup--p-code">FloatingActionButton</code> que está pairando no topo do WebView, complete com o efeito de sombra que você esperaria com o botão. Além disso, quando o menu suspenso da barra de aplicativos é aberto, ele cobre parcialmente o WebView como qualquer outro widget abaixo do menu.</p><p name="24c5" id="24c5" class="graf graf--p graf-after--p">Se você olhar o código, poderá notar que Eu usei aclasse <code class="markup--code markup--p-code">Completer</code> e <code class="markup--code markup--p-code">FutureBuilder</code> frequentemente neste exemplo. Declarando a variável de instância <code class="markup--code markup--p-code">_controller</code> como um Completer é o mesmo que adicionar um placeholder para o WebViewController. Podemos verificar se temos o WebViewController em funcionamento chamando <strong class="markup--strong markup--p-strong">_controller.isCompleted</strong> (significando que ele está completo quando temos um WebViewController válido) ou <a href="https://github.com/efortuna/wiki_browser/blob/master/lib/main.dart#L40" data-href="https://github.com/efortuna/wiki_browser/blob/master/lib/main.dart#L40" class="markup--anchor markup--p-anchor" rel="nofollow noopener noopener" target="_blank">usando um <strong class="markup--strong markup--p-strong">FutureBuilder</strong> com o <strong class="markup--strong markup--p-strong">_controller.future</strong></a>. <br>Usando um FutureBuilder nos permite construir novos componentes de interface do usuário como o FloatingActionButton para adicionar favoritos somente depois de ter um WebViewController funcionando (caso contrário, o programa não teria como obter <code class="markup--code markup--p-code"><a href="https://pub.dartlang.org/documentation/webview_flutter/latest/webview_flutter/WebViewController/currentUrl.html" data-href="https://pub.dartlang.org/documentation/webview_flutter/latest/webview_flutter/WebViewController/currentUrl.html" class="markup--anchor markup--p-anchor" rel="nofollow noopener noopener noopener" target="_blank">currentUrl</a></code> ao salvar uma página favoritada).</p><p name="e11b" id="e11b" class="graf graf--p graf-after--p graf--trailing">Duas outras funcionalidades da WebViews pode ser um pouco complexo, então vamos olhar de perto neles na duas sessões seguinte.</p></div></div></section><section name="595d" class="section section--body section--last"><div class="section-divider"><hr class="section-divider"></div><div class="section-content"><div class="section-inner sectionLayout--insetColumn"><h3 name="7773" id="7773" class="graf graf--h3 graf--leading">WebViews também podem capturar gestos específicos</h3><p name="9b70" id="9b70" class="graf graf--p graf-after--h3">Uma vez que WebViews são widgets , eles pode participar do protocolo de desambiguação de gestos de Flutter (<a href="https://flutter.io/gestures/#gesture-disambiguation" data-href="https://flutter.io/gestures/#gesture-disambiguation" class="markup--anchor markup--p-anchor" rel="nofollow noopener noopener" target="_blank">aka o Gesture Arena</a>). Por padrão, um WebView responde somente a gestos se nenhum outro widget reivindicar isso. Mas você pode fazer o WebView reivindicar proativamente um gesto especificando <code class="markup--code markup--p-code">gestureRecognizers</code>.</p><p name="1a1a" id="1a1a" class="graf graf--p graf-after--p">Se o seu WebView estiver dentro de outro widget que responda a gestos, por exemplo, um ListView, talvez você queira especificar como seu aplicativo responde a gestos. Se o usuário arrastar o dedo pela tela, você deverá rolar o ListView ou o WebView? Se você deseja que os dois widgets sejam roláveis, o widget WebView pode “capturar” o gesto de arrastar para que ele role quando o usuário arrasta o WebView, mas rola o ListView de outra forma. Você pode especificar quais gestos são passados ​​para o widget WebView com oparametro <code class="markup--code markup--p-code">gestureRecognizers</code>. O parâmetro leva um conjunto de todos os GestureRecognizers que você deseja capturar. Não se assuste com esse objeto Factory — basicamente, é apenas um método de construção glorificado. Para capturar o evento de rolagem vertical, posso escrever isso:</p><pre name="bea1" id="bea1" class="graf graf--pre graf-after--p">WebView(<br>  initialUrl: someUrl,<br>  gestureRecognizers: Set()<br>    ..add(Factory&lt;VerticalDragGestureRecognizer&gt;(<br>      () =&gt; VerticalDragGestureRecognizer())),<br>)</pre><p name="c500" id="c500" class="graf graf--p graf-after--pre">Ou, escrito de outro jeito:</p><pre name="735b" id="735b" class="graf graf--pre graf-after--p">var verticalGestures = Factory&lt;VerticalDragGestureRecognizer&gt;(<br>  () =&gt; VerticalDragGestureRecognizer());<br>var gestureSet = Set.from([verticalGestures]);<br>return WebView(<br>  initialUrl: someUrl,<br>  gestureRecognizers: gestureSet,<br>);</pre><p name="0f5c" id="0f5c" class="graf graf--p graf-after--pre">Se você assistiu o <a href="https://www.youtube.com/playlist?list=PLOU2XLYxmsIK0r_D-zWcmJ1plIcDNnRkK" data-href="https://www.youtube.com/playlist?list=PLOU2XLYxmsIK0r_D-zWcmJ1plIcDNnRkK" class="markup--anchor markup--p-anchor" rel="nofollow noopener noopener" target="_blank">Boring Flutter Development Show</a> por completo, você deve ter nos visto desenvolver o Kraken News, quero dizer, o Hacker News Reader App.</p><figure name="2190" id="2190" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 320px; max-height: 670px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 209.39999999999998%;"></div><img class="graf-image" data-image-id="1*D1v2Y4wWJ9jHwhv3kfLhHA.gif" data-width="320" data-height="670" src="https://cdn-images-1.medium.com/max/800/1*D1v2Y4wWJ9jHwhv3kfLhHA.gif"></div><figcaption class="imageCaption">versão mais recente do aplicativo Hacker News que estamos desenvolvendo em nosso YouTube.</figcaption></figure><p name="0fdb" id="0fdb" class="graf graf--p graf-after--figure">Para mostrar a captura de gestos no contexto de um aplicativo, modifiquei o aplicativo Hacker News para mostrar parte da página Web como uma “pré-visualização”. Isso permite que o usuário percorra a página vinculada verticalmente para determinar se deseja abri-la em um página separada para uma leitura mais aprofundada.</p><figure name="29ae" id="29ae" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 229px; max-height: 480px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 209.60000000000002%;"></div><img class="graf-image" data-image-id="1*9rSQoVb91bJV3VIfqcCCzw.gif" data-width="229" data-height="480" src="https://cdn-images-1.medium.com/max/800/1*9rSQoVb91bJV3VIfqcCCzw.gif"></div><figcaption class="imageCaption">Hacker News Reader app com visualizações WebView dos links. Os WebViews capturam o gesto de arrastar vertical para permitir a rolagem das visualizações.</figcaption></figure><p name="4b53" id="4b53" class="graf graf--p graf-after--figure">O código do aplicativo <strong class="markup--strong markup--p-strong">Hacker News reader app</strong> pode ser encontrado <a href="https://github.com/efortuna/hn_app/blob/master/lib/main.dart" data-href="https://github.com/efortuna/hn_app/blob/master/lib/main.dart" class="markup--anchor markup--p-anchor" rel="nofollow noopener noopener" target="_blank">neste repositório do GitHub</a>. (talvez mostrar aqui o código do <code class="markup--code markup--p-code">_buildItem</code>).</p><pre name="15d8" id="15d8" class="graf graf--pre graf-after--p">PageView(children: [<br>  WebView(initialUrl: urlOne),<br>  WebView(initialUrl: urlTwo),<br>  WebView(initialUrl: urlThree),<br>]));</pre><figure name="14dc" id="14dc" class="graf graf--figure graf-after--pre"><div class="aspectRatioPlaceholder is-locked" style="max-width: 320px; max-height: 670px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 209.39999999999998%;"></div><img class="graf-image" data-image-id="1*FHRJGHATuNNr6r3QiWPGag.gif" data-width="320" data-height="670" src="https://cdn-images-1.medium.com/max/800/1*FHRJGHATuNNr6r3QiWPGag.gif"></div><figcaption class="imageCaption">WebViews em um PageView. O PageView controla o deslizamento horizontal e os WebViews podem rolar verticalmente sem nenhum trabalho extra de sua parte.</figcaption></figure><h3 name="c3b4" id="c3b4" class="graf graf--h3 graf-after--figure">Seus WebViews podem precisar do parámetro key</h3><p name="d467" id="d467" class="graf graf--p graf-after--h3">Você provavelmente já viu esses parâmetros opcionais <code class="markup--code markup--p-code">key</code> espalhados em praticamente todos os construtores Widget na base de código Flutter. As chaves são necessárias se você tiver vários widgets com estado do mesmo tipo que são removidos, adicionados ou reordenados no seu aplicativo. Como se constata, WebView é um widget com estado (o estado incluindo a página atual e o histórico do navegador). Portanto, se você tiver vários WebViews no seu aplicativo, talvez seja necessário adicionar um parâmetro <code class="markup--code markup--p-code">key</code> .</p><p name="ee3e" id="ee3e" class="graf graf--p graf-after--p">Um exemplo é no aplicativo Hacker News! Veja o que acontece quando mudamos de tab e nossas WebViews não têm chaves:</p><figure name="2145" id="2145" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 400px; max-height: 837px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 209.3%;"></div><img class="graf-image" data-image-id="1*gO5CdFXZ6QBml_02ViMurA.gif" data-width="400" data-height="837" src="https://cdn-images-1.medium.com/max/800/1*gO5CdFXZ6QBml_02ViMurA.gif"></div><figcaption class="imageCaption">Isso é o que acontece se você não usar as chaves em um aplicativo com estado. Quando mudamos para a tab “New Stories”, a pré-visualização errada errada é exibida.</figcaption></figure><p name="ac78" id="ac78" class="graf graf--p graf-after--figure">Como você pode ver, quando mudamos de tab, o bloco “Interview with Alan Kay” é expandido, mas a webview ainda mostra a página da BBC sobre a Virgin Galactic! Isso é corrigido colocando uma chave no widget de coleção mais alto (neste caso o widget <code class="markup--code markup--p-code">ExpansionTile</code>):</p><figure name="be7b" id="be7b" class="graf graf--figure graf-after--p"><div class="aspectRatioPlaceholder is-locked" style="max-width: 320px; max-height: 670px;"><div class="aspectRatioPlaceholder-fill" style="padding-bottom: 209.39999999999998%;"></div><img class="graf-image" data-image-id="1*Ja4IctL4fyAWhi9y6ctktA.gif" data-width="320" data-height="670" src="https://cdn-images-1.medium.com/max/800/1*Ja4IctL4fyAWhi9y6ctktA.gif"></div><figcaption class="imageCaption">Ufa! O uso de chaves resolve o problema mostrando um WebView diferente (e o site correto) para cada item no aplicativo do leitor</figcaption></figure><p name="58ec" id="58ec" class="graf graf--p graf-after--figure">A explicação super-abreviada de por que as chaves resolvem isso é que quando Flutter alterna a lista de histórias para exibir, ele vê que cada conjunto de histórias é feito de itens ListView com <code class="markup--code markup--p-code">ExpansionTile</code>. O Flutter tem um algoritmo de comparação rápida para evitar redesenhar desnecessariamente a tela que verifica o tipo e a chave do widget. Sem uma chave, como os tipos de widget de cada lista são os mesmos, os itens sem estado (como títulos de links) são atualizados, mas os componentes com estado (como o estado expandido de ExpansionTile e o URL do site) não são redesenhados. Adicionar uma chave corrige esse problema. Para uma explicação mais detalhada, confira este vídeo em chaves:</p><figure name="c1ad" id="c1ad" class="graf graf--figure graf--iframe graf-after--p"><iframe src="https://www.youtube.com/embed/kn0EOS-ZiIc?feature=oembed" width="700" height="393" frameborder="0" scrolling="no"></iframe></figure><p name="18ef" id="18ef" class="graf graf--p graf-after--figure">Da mesma forma, se você usar WebViews no contexto de um widget <a href="https://flutter.io/docs/development/ui/animations/hero-animations" data-href="https://flutter.io/docs/development/ui/animations/hero-animations" class="markup--anchor markup--p-anchor" rel="nofollow noopener noopener" target="_blank">Hero</a>, você vai querer usar global key assim Flutter irá saber que as duas WebViews são actualmente as mesmas e não precisa renderizar novamente a segunda.</p><h3 name="fb81" id="fb81" class="graf graf--h3 graf-after--p">Algumas coisas restantes para ter em mente:</h3><p name="7179" id="7179" class="graf graf--p graf-after--h3">O plug-in do WebView está atualmente na versão Preview enquanto terminamos de adicionar melhorias. Isso significa que, se você quiser executar o plug-in webview no iOS, precisará adicionar a seguinte linha dentro do <code class="markup--code markup--p-code">&lt;dict&gt;</code> no seu <code class="markup--code markup--p-code">ios/Runner/Info.plist</code>: <a href="https://github.com/flutter/flutter/issues/19030#issuecomment-437534853" data-href="https://github.com/flutter/flutter/issues/19030#issuecomment-437534853" class="markup--anchor markup--p-anchor" rel="nofollow noopener noopener" target="_blank">como descrito neste issue do GitHub</a>.</p><p name="dd6f" id="dd6f" class="graf graf--p graf-after--p">Há atualmente também um problema conhecido com o teclado do Android, e a equipe Flutter está <a href="https://github.com/flutter/flutter/issues/19718" data-href="https://github.com/flutter/flutter/issues/19718" class="markup--anchor markup--p-anchor" rel="nofollow noopener noopener" target="_blank">trabalhando em uma solução</a>.</p><p name="8768" id="8768" class="graf graf--p graf-after--p">Há outro plug-in WebView baseado na comunidade, embora ele não tenha todas as funcionalidades do plug-in acima, pela equipe Flutter. Ele simplesmente exibe uma página da Web em uma visualização nativa e não está integrado ao restante da árvore de widgets Flutter. Portanto, essa versão não permite compor WebViews com outros widgets arbitrários. Usando o <a href="https://pub.dartlang.org/packages/webview_flutter" data-href="https://pub.dartlang.org/packages/webview_flutter" class="markup--anchor markup--p-anchor" rel="nofollow noopener noopener" target="_blank">plugin webview_flutter</a> descrito neste artigo evita esse problema.</p><p name="fad7" id="fad7" class="graf graf--p graf-after--p graf--trailing">Isso é tudo, pessoal! Vá em frente e adicione WebViews aos seus aplicativos Flutter. E dê amor a esses krakens também.</p></div></div></section>
</section>