<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Flutter Portugal - Medium]]></title>
        <description><![CDATA[Uma colectânea de artigos sobre Flutter traduzidos para Português - Medium]]></description>
        <link>https://medium.com/flutter-portugal?source=rss----8ea47648f583---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Flutter Portugal - Medium</title>
            <link>https://medium.com/flutter-portugal?source=rss----8ea47648f583---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 01 Jun 2026 05:58:33 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/flutter-portugal" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[O Fardo da Mutabilidade]]></title>
            <link>https://medium.com/flutter-portugal/o-fardo-da-mutabilidade-9acdaa662dc2?source=rss----8ea47648f583---4</link>
            <guid isPermaLink="false">https://medium.com/p/9acdaa662dc2</guid>
            <category><![CDATA[dartlang]]></category>
            <category><![CDATA[immutability]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[dart]]></category>
            <dc:creator><![CDATA[Mariana Castanheira]]></dc:creator>
            <pubDate>Fri, 09 Oct 2020 13:58:11 GMT</pubDate>
            <atom:updated>2020-10-09T13:58:11.709Z</atom:updated>
            <content:encoded><![CDATA[<p>Na <a href="https://twitter.com/flutterportugal">Flutter Portugal</a> acreditamos que o conhecimento tem de ser acessível a todos, independentemente do grau de conhecimento de línguas estrangeiras. Assim, vamos lançar uma pequena série de artigos que vão abranger vários tópicos: Dados persistidos, State Management, Navegação, etc…</p><p>Este artigo foi originalmente escrito por <a href="https://medium.com/@davidmorgan_14314">David Morgan</a> com o título “<a href="https://medium.com/@davidmorgan_14314/the-mutability-tax-6403d84f21c0">The Mutability tax</a>” e traduzido com autorização do autor.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Ixvfbj8u3_fw8myT.png" /></figure><p>Engenharia de <em>software</em> é complicada e eu sou preguiçoso. Não gosto mesmo nada de trabalho desnecessário. Por isso, se o código vai ficar cá por uns tempos, eu construo-o de forma a que seja fácil de manter.</p><p>Eu trabalho com linguagens orientadas a objetos, normalmente <a href="https://dart.dev/">Dart</a>, e uma consideração fundamental que se deve ter em tais linguagens é a de se os dados vão ser mutáveis ou imutáveis. Muitos, comigo incluído, recomendam dados imutáveis; ver, por exemplo, <em>Effective java item 15</em>, “minimize mutability” (“minimizar mutabilidade”).</p><p>Aqui está um trecho de código Dart que ilustra o porquê da mutabilidade poder ser problemática:</p><pre>// Biggest first.<br>var cities = [‘Tokyo’, ‘Delhi’, ‘Shanghai’, ‘Mumbai’, ‘Beijing’];<br>print(‘Biggest cities of the world in alphabetical order:\n’);<br>DisplayAlphabetically().display(cities);<br>print(‘And the biggest is ${cities.first}.’);</pre><p>E aqui está o resultado:</p><pre>Biggest cities in the world in alphabetical order:<br>Beijing, Delhi, Mumbai, Shanghai, Tokyo<br>And the biggest is Beijing.</pre><p>— Ups! A maior cidade é Tóquio, não Beijing (Pequim). O que é que correu mal? DisplayAlphabeticallyestá a falhar; modifica a lista que lhe é dada, ordenando-a:</p><pre>void display(List&lt;String&gt; strings) {<br>  (strings..sort()).forEach(print);<br>}</pre><p>Mas não é aqui que está o verdadeiro problema. É garantido que, em engenharia de <em>software, </em>onde quer que seja possível errar, vai errar-se. O problema é que, ao passar uma lista mutável ao método display — o qual pode ter sido escrito por outra pessoa, ou por nós num dia mau, ou copiado e colado do código com características diferentes — , criamos uma oportunidade para <em>bugs.</em></p><p>Para solucionar o problema, podemos utilizar a imutável BuiltList:</p><pre>// Biggest first.<br>var cities = BuiltList.of(<br>    [&#39;Tokyo&#39;, &#39;Delhi&#39;, &#39;Shanghai&#39;, &#39;Mumbai&#39;, &#39;Beijing&#39;]);<br>print(&#39;Biggest cities of the world in alphabetical order:\n&#39;);<br>DisplayAlphabetically().display(cities);<br>print(&#39;And the biggest is ${cities.first}.&#39;);</pre><p>— e agora sabemos que DisplayAlphabeticallynão pode modificar cities, deixando de fora toda a categoria de <em>bugs</em>.</p><p>Isto é um exemplo simples, fácil de solucionar e corrigir os <em>bugs.</em> O código do mundo real é muito mais complexo e os problemas <em>muito</em> mais sérios. Em particular, vejo problemas com:</p><ul><li>Código de UI (Interface do Utilizador), o qual normalmente é constituído por módulos folgadamente acoplados (<em>loosely coupled</em>) que partilham dados. A mutabilidade cria oportunidades para <em>bugs</em> graves, que são difíceis de resolver, implicando um intensivo trabalho de manutenção.</li><li>Código de servidor de alta performance, o qual usa módulos acoplados de forma folgada que partilham dados para permitir paralelismo. Este é um ambiente muito diferente, mas o problema da mutabilidade é o mesmo: <em>bugs </em>graves<em> </em>que são difíceis de resolver e uma manutenção significativa.</li></ul><p>Dados mutáveis nestas linguagens são a predefinição e funcionam bem o suficiente para casos simples; mas torna-se num problema à medida que a base de código aumenta. Por isso, é uma armadilha em que é <em>muito</em> comum cair. Em resumo:</p><blockquote><em>Sistemas com módulos folgadamente acoplados que passam dados mutáveis entre eles ficam com </em><strong><em>o fardo da mutabilidade</em></strong><em>. Eles apoiam-se na convenção e na sorte para evitar bugs graves e difíceis de resolver, devido aos indesejados efeitos secundários da mutação. À medida que estes sistemas ficam maiores e mais complexos, a sua sorte acaba e os custos da manutenção aumentam.</em></blockquote><p>Obviamente, se a mutabilidade é o problema, então a imutabilidade é a resposta! Mas esta vem com os seus próprios problemas.</p><h3>O fardo da Imutabilidade</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/567/1*Wje_VdXL2B_Ep74H2nJXag.png" /></figure><p>A maioria das linguagens orientadas a objetos não é, infelizmente, desenhada com a imutabilidade em mente. O caminho óbvio é construir convenções próprias e bibliotecas para dados imutáveis e, surpreendentemente, existem muitas armadilhas.</p><p>Analisemos essas armadilhas. Elas são semelhantes transversalmente a todas as linguagens orientadas a objetos mas, para efeitos deste artigo, vamos considerar Dart. A forma mais simples de ter dados imutáveis em Dart é esta:</p><pre>class Customer {<br>  final String name;<br>  final int age;<br>  Customer({this.name, this.age});<br>}</pre><p>Toda a gente concorda com usar os campos (<em>fields</em>) namee age. Mas já tivemos de escolher entre usar parâmetros posicionais (<em>positional parameters</em>) ou parâmetros designados (<em>named parameters</em>). À medida que a lista de parâmetros aumenta, é provável que se queiram parâmetros designados para legibilidade, por isso a convenção que escolhemos para o exemplo é para os usar desde o início.</p><p>O próximo problema é como criar novas instâncias imutáveis, baseadas noutras já existentes — como as “atualizar”. A predefinição, e aquilo que regularmente vemos na prática, é forçar as pessoas a usarem o construtor:</p><pre>var customer = Customer(name: &#39;John Smith&#39;, age: 34);<br>var updatedCustomer = Customer(<br>    name: customer.name, age: customer.age + 1);</pre><p>Infelizmente, o código a utilizar este construtor desta forma <em>quebra se adicionarmos um campo</em>:</p><pre>var customer = Customer(<br>    name: &#39;John Smith&#39;, age: 34, visits: 12);</pre><pre>// ... later on ... whoops! This doesn&#39;t work, resets &#39;visits&#39;.<br>var updatedCustomer = Customer(<br>    name: customer.name, age: customer.age + 1);</pre><p>Poderíamos contornar esta situação ao criar todos os parâmetros necessários, ou como parâmetros posicionais ou usando a anotação @required , mas isto não é muito melhor: agora qualquer chamada ao construtor na base de código precisa de ser atualizada para adicionar um novo campo.</p><p>O que é que fazemos? Não podemos utilizar o construtor, precisamos de um método:</p><pre>class Customer {<br>  final String name;<br>  final int age;<br>  Customer({this.name, this.age});<br>  Customer copyWith({String name, String age}) =&gt;<br>      Customer(name: name ?? this.name, age: age ?? this.age);<br>}</pre><p>Então agora podemos escrever:</p><pre>var updatedCustomer = customer.copyWith(age: customer.age + 1);</pre><p>— e este código continua a ter o comportamento correto se um campo for adicionado.</p><p>Infelizmente, assim que a um campo é permitido ser null, este padrão falha em Dart. Parâmetros designados opcionais sem uma pré-definição explícita simplesmente voltam a nulle não há forma de verificar se um nullfoi explicitamente passado ou se foi recebido, porque nada foi especificado:</p><pre>// Doesn&#39;t work! The method can&#39;t tell that &#39;null&#39; was explicitly<br>// passed for &#39;visits&#39;, so nothing is updated.<br>var customerWithoutVisits = customer.copyWith(visits: null);</pre><p>Campos que podem ser <em>null</em> podem ser corrigidos ao usar um método withpor cada campo:</p><pre>class Customer{<br>  final String name;<br>  final int age;<br>  final int visits;<br>  Customer({this.name, this.age, this.visits});<br>  Customer withName(String name)<br>      =&gt; Customer(name: name, age: age, visits: visits);<br>  Customer withAge(int age)<br>      =&gt; Customer(name: name, age: age, visits: visits);<br>  Customer withVisits(int visits)<br>      =&gt; Customer(name: name, age: age, visits: visits);<br>}</pre><p>— o que, pelo menos, <em>funciona</em>, mas está longe de ser satisfatório. É muito código <em>boilerplate</em> (<a href="https://pt.wikipedia.org/wiki/Boilerplate_code">https://pt.wikipedia.org/wiki/Boilerplate_code</a>) para escrever; é fácil enganarmo-nos no código <em>boilerplate</em>; e é <em>lento</em>: atualizar múltiplos campos requer uma cópia completa de objeto por campo.</p><p>Mesmo com tipos primitivos, manter objetos imutáveis em Dart manualmente não tem piada nenhuma. Este exemplo tem três campos; num sistema real, Customertem facilmente dez ou mais. Isto significa pelo menos dez métodos with, cada um a passar pelo menos dez argumentos de volta ao construtor. Ouch.</p><p>Mas o pior ainda está para vir.</p><p>Vai ser preciso suportar coleções e tipos aninhados (<em>nested types</em>):</p><pre>class ShoppingBasket {<br>  final Customer customer;<br>  final List&lt;Item&gt; items;<br>  final List&lt;Offer&gt; offers;<br>  ShoppingBasket(<br>      this.customer,<br>      Iterable&lt;Item&gt; items,<br>      Iterable&lt;Offer&gt; offers)<br>    // Copy defensively to ensure immutability.<br>    : this.items = List.unmodifiable(items),<br>      this.offers = List.unmodifiable(offers);<br>  // TODO(davidmorgan): add &quot;with&quot; method per field.<br>}</pre><p>De maneira a aceitar as coleções mutáveis de Dart no nosso mundo imutável, temos de os copiar defensivamente. Uma das supostas vantagens de tipos imutáveis é a de serem <em>rápidos</em>. Nós conseguimos torná-los lentos.</p><p>E, se avançarmos com os simples métodos withque tínhamos antes, atualizar um campo aninhado (<em>nested field</em>) torna-se numa tarefa:</p><pre>var updatedBasket = basket<br>    .withCustomer(basket.customer.withName(updatedName))<br>    .withItems([...basket.items, newItem, newItem2]);</pre><p>Podemos emendar isto com métodos de conveniência adicionais:</p><pre>var updatedBasket = basket<br>    .withCustomerName(updatedName)<br>    .addItem(newItem)<br>    .addItem(item2);</pre><p>— mas isso significaria ainda <em>mais</em> código <em>boilerplate</em>. E estaríamos mais uma vez a fazer com que cada atualização fizesse uma cópia completa, tornando os nossos dados imutáveis <em>lentos</em>.</p><p>A única forma de responder a todos estas questões — para fornecer dados imutáveis em Dart que sejam fáceis e rápidos de “atualizar”, que suportem campos <em>null</em>, e que não quebrem código existente quando são adicionados campos — é com o <a href="https://en.wikipedia.org/wiki/Builder_pattern">padrão <em>builder</em></a>. Cada tipo de dados tem um tipo adicional associado, o seu <em>builder</em>, o qual tem os mesmos dados mas é mutável. Usamos um <em>builder</em> para “construir” uma instância imutável.</p><p>Também precisamos de uma nova biblioteca com coleções de categorias baseadas em <em>builders</em>. Isto permite que coleções imutáveis sejam geridas sem cópias desnecessárias, por isso são rápidas.</p><p>Ao utilizar categorias de <em>builders</em> aninhados (<em>nested builder classes</em>) e <em>builders </em>de coleção (<em>collection builders</em>), o nosso código de exemplo pode ser tanto conveniente como eficiente, e as “atualizações” podem parecer-se com isto:</p><pre>var updatedBasket = basket.rebuild((b) =&gt; b<br>    ..customer.name = updatedName<br>    ..items.addAll([newItem, item2]));</pre><p>Infelizmente (outra vez!), o padrão <em>builder</em> precisa de mais código <em>boilerplate</em> do que qualquer outro exemplo e é extremamente <em>difícil</em> de obter corretamente. Existem muitas versões, muitas convenções possíveis e muitos erros possíveis de se fazer. Podemos usar o padrão <em>builder</em>, mas mesmo assim não suportar corretamente os campos que podem ser <em>null</em>; ou continuar a ter um desempenho fraco; ou digitar o código <em>boilerplate</em> com <em>bugs</em>. Tais <em>bugs</em> podem ser tão graves como <em>mutabilidade acidental</em> — a qual é, claro, pior do que a mutabilidade conhecida que tínhamos antes de mudar para dados imutáveis!</p><p>Isto traz repetição: digitar milhares e milhares de linhas de código <em>boilerplate</em> à mão, de forma a evitar carregar o fardo da mutabilidade, deixa-nos abertos a <em>exatamente o mesmo tipo de </em>bug<em> que estávamos a tentar evitar</em>, assim que cometemos um erro no código <em>boilerplate</em>.</p><p>A maneira certa de fazer imutabilidade é através de padrões <em>builder</em>, mas escrevê-los à mão simplesmente não vale a pena.</p><h4><strong>Imutabilidade através de Geração de Código (<em>Codegen</em>)</strong></h4><p>Então vamos dar a que, em linguagens orientadas a objetos que não são desenhadas para imutabilidade, a forma correta de alcançar a imutabilidade e evitar o fardo da imutabilidade é deixar outra pessoa fazer o trabalho por nós, através de uma biblioteca que origina código. Só com a geração de código (<em>codegen)</em> podemos reduzir o trabalho acrescido que é requerido na criação de dados imutáveis nestas linguagens.</p><p>Infelizmente (uma última vez!), a geração de código vem com os seus próprios defeitos: <em>continua</em> a precisar de código <em>boilerplate</em> para ser posta a funcionar; poderá ser preciso trabalho adicional para pôr o nosso projeto em <em>build;</em> e o nosso IDE (Ambiente de Desenvolvimento Integrado) pode não ser tão útil quando chega ao código gerado. Mas é o melhor que conseguimos fazer hoje. Em Dart existe a minha própria biblioteca, <a href="https://github.com/google/built_value.dart">built_value</a>, a qual vai mais além e pode serializar os dados; escrevi um artigo sobre isso <a href="https://medium.com/dartlang/darts-built-value-for-immutable-object-models-83e2497922d4">aqui</a>. Tem este aspeto, com o código <em>boilerplate</em> realçado a negrito:</p><pre><strong>abstract</strong> class Customer <strong>implements Built&lt;Customer, CustomerBuilder&gt;</strong> {<br>  String get name;<br>  int get age;<br>  @nullable<br>  int get visits;<strong>factory Customer(void Function(CustomerBuilder) updates) = <br>    _$Customer;<br>  Customer._();</strong><br>}</pre><p>O código <em>boilerplate</em> é desagradável e assustador, mas é fácil de preservar: não é necessário adicionar nenhum código <em>boilerplate</em> quando se adiciona um novo campo, e é impossível existirem <em>bugs</em> no código <em>boilerplate</em> porque a geração de código também o verifica por nós. Alcançámos dados imutáveis sustentáveis. Aquilo que agora nos é imposto já não é tanto um fardo contínuo, mas mais uma taxa de entrada.</p><p>Em Java existe o <a href="https://github.com/google/auto/blob/master/value/userguide/builders.md">AutoValue.Builder</a>, que tem uma abordagem parecida e vem com um código <em>boilerplate</em> semelhante. A propósito disto, essa equipa fez um <a href="https://docs.google.com/presentation/d/14u_h-lMn7f1rXE1nDiLX0azS3IkgjGl5uxp5jGJ75RE/edit#slide=id.g2a5e9c4a8_00">power point</a> muito bom acerca do porquê desta geração de código ser necessária.</p><p>Em suma:</p><blockquote><em>Em linguagens não desenhadas para imutabilidade, os sistemas que evitam o fardo da mutabilidade ao implementar estruturas de dados imutáveis à mão, por outro lado acarretam o </em><strong><em>fardo da imutabilidade</em></strong><em>. Estruturas de dados imutáveis nestas linguagens são difíceis de obter corretamente e, por definição, são inconvenientes e lentas; isto resulta num custo mais elevado de manutenção, </em>bugs<em> e problemas de desempenho. A maneira certa de contornar o fardo da mutabilidade nestas linguagens é usar uma biblioteca que origine e esconda o código </em>boilerplate<em> necessário para dados imutáveis convenientes e rápidos.</em></blockquote><h4><strong>Pensamentos finais</strong></h4><p>Pode-me ser perguntado se pratico aquilo que digo; se realmente faço o trabalho adicional de usar geração de código para dados imutáveis no meu trabalho diário.</p><p>A resposta é: se vou ter de manter o código, <em>sim</em>, uso os dados imutáveis. Se é pequeno e “escreve uma vez, corre uma vez, apaga”, provavelmente não me vou dar ao trabalho.</p><p>Houve uma exceção recente a esta regra. Estava a trabalhar em código, nas profundezas do <em>build</em> em Dart; e não podemos usar facilmente <em>codegen</em> em código dependente de <em>codegen</em>. Por isso, voltei a dados imutáveis feitos à mão.</p><p>Estava demasiado preguiçoso para digitar métodos with; usei só o construtor. Depois adicionei um campo. <em>Depois</em>, passei um tempo considerável a corrigir <em>bugs</em>, porque o código existente estava a apagar o novo campo, exatamente como foi descrito neste artigo.</p><p>Isto lembrou-me do quanto desgosto de ambos os fardos da mutabilidade e da imutabilidade, e daí nasceu a ideia para este artigo.</p><p>Vamos concluir com os dois sumários para que possam coexistir ao lado um do outro.</p><p><strong>O Fardo da Mutabilidade</strong></p><blockquote><em>Sistemas que tenham módulos folgadamente acoplados (</em>loosely coupled<em>) que transmitem dados mutáveis entre si, acarretam </em><strong><em>o fardo da mutabilidade</em></strong><em>. Eles dependem da convenção e de sorte para evitar bugs graves e difíceis de resolver devido a efeitos secundários indesejados da mutação. À medida que estes sistemas crescem e se tornam mais complexos, a sua sorte acaba e o custo de manutenção aumenta.</em></blockquote><p><strong>O Fardo da Imutabilidade</strong></p><blockquote><em>Em linguagens que não foram desenhadas para imutabilidade, os sistemas que contornam o fardo da mutabilidade ao implementar arquiteturas de dados imutáveis à mão, acarretam </em><strong><em>o fardo da imutabilidade</em></strong><em>. É difícil acertar nas arquiteturas de dados imutáveis nestas linguagens e são, por definição, inconvenientes e lentas; isto resulta num maior custo de manutenção, </em>bugs<em> e problemas de desempenho. A forma correta de evitar o fardo da imutabilidade nestas linguagens é usar uma biblioteca que faça a gestão e esconda o código </em>boilerplate<em> necessário para dados imutáveis convenientes e rápidos.</em></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*pFA4sT0TFrOu0tPi.png" /><figcaption>Dados mutáveis levam a problemas de manutenção. Em linguagens não-desenhadas para imutabilidade, tais como Java e Dart, dados imutáveis mantidos manualmente trazem ainda mais problemas. Geração de código (Codegen) é a melhor resposta que temos hoje em dia. Fornece o código <em>boilerplate</em> necessário para dados imutáveis sem <em>bugs</em>, eficientes e convenientes.</figcaption></figure><ul><li><a href="https://medium.com/@davidmorgan_14314">David Morgan - Medium</a></li><li><a href="https://medium.com/@davidmorgan_14314/the-mutability-tax-6403d84f21c0">The Mutability Tax</a></li><li><a href="https://medium.com/flutter-portugal">Flutter Portugal</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9acdaa662dc2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/flutter-portugal/o-fardo-da-mutabilidade-9acdaa662dc2">O Fardo da Mutabilidade</a> was originally published in <a href="https://medium.com/flutter-portugal">Flutter Portugal</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Estamos On — Our Contribution]]></title>
            <link>https://medium.com/flutter-portugal/estamos-on-our-contribution-cbf637275ecd?source=rss----8ea47648f583---4</link>
            <guid isPermaLink="false">https://medium.com/p/cbf637275ecd</guid>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[covid19]]></category>
            <dc:creator><![CDATA[Flutter Portugal]]></dc:creator>
            <pubDate>Thu, 02 Apr 2020 17:03:54 GMT</pubDate>
            <atom:updated>2020-04-02T17:30:13.959Z</atom:updated>
            <content:encoded><![CDATA[<h3>Estamos On — Our Contribution</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HtlhJU3G_aNf1mU7YAe31g.png" /></figure><p>We are living unique times. For most generations this is the first major pandemic we have ever faced. For our own good and those around us we are urged to maintain social distance in order to stop it. But today more than ever, we have to be resilient and creative, to be in solidarity and to stay united even if at a distance. Above all, we need to keep ourselves informed, to understand where the questions and answers are, what measures are being taken to deal with the pandemic and the enormous challenges that this fight unleashes.</p><p>So, in order to compile all this information (already published on the<a href="https://covid19estamoson.gov.pt/"> website</a> created by VOST) in a single channel accessible in a few seconds and with a small number of taps, the mobile application “Estamos On” was born, available for download at <a href="https://play.google.com/store/apps/details?id=com.vost.covid19mobile">Google Play Store</a> and <a href="https://apps.apple.com/us/app/estamos-on-covid19/id1502916368?l=pt&amp;ls=1">Apple App Store</a>.</p><ul><li><a href="https://play.google.com/store/apps/details?id=com.vost.covid19mobile">Estamos ON - Covid19 - Apps on Google Play</a></li><li><a href="https://apps.apple.com/us/app/estamos-on-covid19/id1502916368?l=pt&amp;ls=1">‎Estamos ON - Covid19</a></li></ul><p>This App was made possible due to the volunteer work of Flutter Portugal and VOST Portugal. For those who wonder what these communities or groups are or what they do, let’s get to know them a little better.</p><p><a href="https://flutter.pt/">Flutter Portugal</a> is one of the official communities that shares knowledge and promotes Google’s Flutter technology to create applications for mobile, web and desktop. The community is composed of volunteers from all over the continental and insular territory. It organizes events, such as conferences and workshops, of greater or lesser dimension, taking into account the objective and the public for which they are intended.</p><p><a href="https://flutter.pt">Flutter PT</a></p><p>VOST is the acronym for <a href="https://info.vost.pt/">Virtual Operations Support Team</a>, which in Portugal assumes itself as Digital Volunteers in Emergency Situations, that is, people committed to providing information and making the population more enlightened, informed, prepared and protected. Since October 16th, 2019, it is formally constituted as a civil protection association called <em>VOST Portugal — Associação de Voluntários Digitais em Situações de Emergência</em>.</p><p><a href="https://info.vost.pt/">VOST PORTUGAL</a></p><p>Both organizations share the same values — everything that is produced by the community is open sourced — which means the code is open and allows it to be viewed and reviewed by anyone. Furthermore, those who want to help can do so by contributing to the project.</p><p>The first cases of Covid-19 appeared in Portugal in early March. Their unequivocal spread led to the declaration of a state of emergency, through the Decree of the President of the Republic №14-A/2020 of March 18th. In order to turn the available information more transparent and widely accessible, the Government of Portugal launched an appeal for the need to build a portal that would aggregate all the information. This was built by VOST Portugal, which in turn requested Flutter Portugal’s help so that the mobile application could be created.</p><p>However, with one of the communication channels already developed in the form of a website, the question may arise: is there a need to create a mobile application?</p><p>Putting aside personal choice (the fact that we may like to access a website or prefer to use an app), an application gives us a quick means of communication with all users in the form of Push Notifications. As soon as there is an update on the state of emergency, new measures by the State or an update of the new case numbers, a Push Notification can quickly send the information to all users, without them having to go to the website. In future updates, new features can also be added, such as local caching so that even offline, all information is easily accessible.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FuaRZiiaYhz4%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DuaRZiiaYhz4&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FuaRZiiaYhz4%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/c18dd07a485e786f793c228e99975d50/href">https://medium.com/media/c18dd07a485e786f793c228e99975d50/href</a></iframe><p>Saying that the project had a short deadline would not do justice to the urgency of the situation, so it was very important to choose a technology that allowed us to deliver a robust application in the shortest possible time. Since, initially, we were only a team of 5, creating two native applications was out of the question, so we adopted Flutter. For the Flutter Portugal team the choice was easy, for several reasons:</p><ul><li>Flutter allows 2 applications to be created with one code base — for Android and iOS, reducing the project execution time to half;</li><li>Each one of us had already proven the robustness of the technology, with personal projects or by the fact that it is the technology we use in our daily work;</li><li>We are all passionate about this technology, and quite often we use our <a href="https://discordapp.com/">Discord</a> to discuss ideas and ask questions to each other;</li><li>Our team had already worked together for the Fogos.pt project, which consisted in a rewrite of the existing project to Flutter, so not only had we proven that we were able to deliver a project as a team, but we also proved that Flutter was a great choice for this kind of project.</li></ul><p><a href="https://play.google.com/store/apps/details?id=com.tomahock.fogos">Fogos.pt - Apps on Google Play</a></p><p>With the completion of this project, many lessons have been learned that we would like to share here:</p><ul><li>The urgency and scale of this project required fast and efficient communication, so we chose to use our Discord server for it. We have divided the communication into two channels. The first channel’s purpose, with voice and audio, was to quickly discuss all the issues of the project and ask for help from other team members. The second channel had only important announcements — new features, status of the project, what we had to do and new information from the VOST channel.</li></ul><p><a href="https://discordapp.com/">Discord - A New Way to Chat with Friends &amp; Communities</a></p><ul><li>It was crucial that all the members of the team already knew each other and worked together. This made it easy to divide up tasks so that we always took advantage of each other’s strengths — communication with other entities, design, preparation of a testing and distribution platform or project organisation and management.</li><li>The fact that we had direct contact with the designer <a href="http://www.ffrutuoso.com">Francisco Frutuoso</a> and that he made the project available to us in <a href="https://zeplin.io/">Zeplin</a>, made it easier and faster to create all the screens.</li></ul><ul><li><a href="https://www.ffrutuoso.com/">Francisco Frutuoso - Digital Designer</a></li><li><a href="https://scene.zeplin.io/project/5e711e6973c497b6352ed9f3">COVID19 App | Zeplin Scene</a></li></ul><ul><li>The most important thing was that there was great mutual respect for all members of the team. This made us listen and discuss opinions, even if these were against what we would be used to. Together we grew, learned and prepared for future projects.</li></ul><p>The willingness to help others in this time of crisis and the willingness to want to work together again is what brought this team together. Opportunities like these fill us with experience that we will apply in our job and in our life in general. We are confident that this can be replicated by other teams of programmers and designers around the world, provided there is a basis for mutual respect and a common goal.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yF0W_UiruPf4JDtb9bFwHg.jpeg" /><figcaption>Flutter Portugal’s Team</figcaption></figure><p>Finally, we leave a message for Non-Profit Organizations and government bodies:</p><p>The Flutter Portugal team is here, more united than ever.</p><p><strong>Challenge us.</strong></p><p><em>Written by </em><a href="https://medium.com/@solid.goncalo"><em>Gonçalo Palma</em></a><em> and </em><a href="https://mochilavermelha.blogs.sapo.pt/"><em>Simone Júlio</em></a></p><p>You want to help this cause? Go to the Github repository of the “Estamos On” app and see what issues are open!</p><p><a href="https://github.com/vostpt/covid19-mobile">vostpt/covid19-mobile</a></p><p><strong>Website Flutter Portugal:</strong></p><p><a href="https://flutter.pt">Flutter PT</a></p><p><strong>Twitter Flutter Portugal:</strong></p><p><a href="https://twitter.com/FlutterPortugal">Flutter Portugal</a></p><p><strong>Join our Discord server! → </strong><a href="https://chat.flutter.pt">http://chat.flutter.pt</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=cbf637275ecd" width="1" height="1" alt=""><hr><p><a href="https://medium.com/flutter-portugal/estamos-on-our-contribution-cbf637275ecd">Estamos On — Our Contribution</a> was originally published in <a href="https://medium.com/flutter-portugal">Flutter Portugal</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Estamos On — A nossa contribuição]]></title>
            <link>https://medium.com/flutter-portugal/estamos-on-a-nossa-contribui%C3%A7%C3%A3o-4392796ad016?source=rss----8ea47648f583---4</link>
            <guid isPermaLink="false">https://medium.com/p/4392796ad016</guid>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[covid19]]></category>
            <dc:creator><![CDATA[Flutter Portugal]]></dc:creator>
            <pubDate>Thu, 02 Apr 2020 16:48:08 GMT</pubDate>
            <atom:updated>2020-04-03T10:03:51.830Z</atom:updated>
            <content:encoded><![CDATA[<h3>Estamos On — A nossa contribuição</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HtlhJU3G_aNf1mU7YAe31g.png" /></figure><p>Vivemos momentos únicos. Para a maioria das gerações trata-se da primeira grande pandemia que enfrentamos. Para o bem de todos, somos instados a manter distanciamento social. Mas, hoje mais que nunca, temos que ser resilientes e criativos, ser solidários e mantermo-nos unidos ainda que à distância. Necessitamos acima de tudo de nos manter informados, compreender onde estão as perguntas e respostas, que medidas estão a ser tomadas para fazer face à pandemia e aos enormes desafios que esta luta despoleta.</p><p>Assim, com o intuito de compilar toda esta informação (já publicada no <a href="https://covid19estamoson.gov.pt/">website</a> criado pela VOST) num só canal acessível em poucos segundos e com um reduzido número de cliques, nasceu a aplicação “Estamos On”, já disponível para <em>download</em> na <a href="https://play.google.com/store/apps/details?id=com.vost.covid19mobile">Play Store</a> da Google e na <a href="https://apps.apple.com/us/app/estamos-on-covid19/id1502916368?l=pt&amp;ls=1">App Store</a> da Apple.</p><ul><li><a href="https://play.google.com/store/apps/details?id=com.vost.covid19mobile">Estamos ON - Covid19 - Apps on Google Play</a></li><li><a href="https://apps.apple.com/us/app/estamos-on-covid19/id1502916368?l=pt&amp;ls=1">‎Estamos ON - Covid19</a></li></ul><p>Esta App é uma realidade devido ao trabalho voluntário da Flutter Portugal e da VOST Portugal. Para quem se pergunta o que são ou o que fazem estas comunidades ou grupos, vamos de seguida conhecê-los um pouco melhor.</p><p>A <a href="https://flutter.pt/">Flutter Portugal </a>é uma das comunidades oficiais que partilha conhecimento e promove a tecnologia Flutter da Google que se destina a criar aplicações para mobile, web e desktop. A comunidade é composta por voluntários de todo o território continental e insular. Esta organiza eventos, tais como, conferências e workshops, de maior ou menor dimensão, tendo em conta o objetivo e público a que se destinam.</p><p><a href="https://flutter.pt">Flutter PT</a></p><p>VOST é o acrónimo para <em>Virtual Operations Support Team, </em>que em Portugal se assume como <a href="https://info.vost.pt/">Voluntários Digitais em Situações de Emergência</a>, isto é, pessoas comprometidas a prestar informação e a tornar a população mais esclarecida, informada, preparada e protegida. Desde 16 de Outubro de 2019, constitui-se formalmente como associação de proteção civil denominada de VOST Portugal — Associação de Voluntários Digitais em Situações de Emergência.</p><p><a href="https://info.vost.pt/">VOST PORTUGAL</a></p><p>Ambas partilham os mesmos valores, ou seja, tudo o que é produzido na comunidade é feito em open source, ou seja, o código é aberto, o que permite ser consultado por qualquer pessoa.</p><p>Como sabemos, os primeiros casos de Covid-19 surgiram em Portugal no início de março. A sua propagação inequívoca levou à declaração de estado de emergência, através do Decreto do Presidente da República n.º 14-A/2020, de 18 de março. De forma a tornar a informação mais transparente e acessível, o Governo de Portugal lançou um apelo para a necessidade de construir um portal que agregasse toda a informação. Este foi construído pela VOST Portugal, que por sua vez solicitou a ajuda da Flutter Portugal para que a aplicação pudesse ser criada.</p><p>Contudo, com um dos canais de comunicação já desenvolvidos sob a forma de um website, poderá colocar-se a questão: haverá a necessidade de se criar uma aplicação?</p><p>Colocando de parte a escolha pessoal de cada pessoa (o facto de gostarem mais de aceder a um website ou usar uma aplicação), uma aplicação dá-nos um meio de comunicação rápido com todos os utilizadores sob a forma de Notificações. Assim que haja uma actualização no estado de emergência, medidas novas por parte do Estado ou que saiam os números dos novos casos por dia, a Notificação consegue rapidamente transmitir a informação a todos os utilizadores, sem que estes tenham de ir novamente ao website. Em futuras actualizações, também se pode colocar novas funcionalidades, como a de cache local para que mesmo em offline toda a informação seja facilmente acessível.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FuaRZiiaYhz4%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DuaRZiiaYhz4&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FuaRZiiaYhz4%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/c18dd07a485e786f793c228e99975d50/href">https://medium.com/media/c18dd07a485e786f793c228e99975d50/href</a></iframe><p>Dizer que o projecto tinha um prazo de entrega curto não iria fazer justiça à urgência da situação, por isso seria fundamental escolher uma tecnologia que nos permitisse entregar uma aplicação robusta no menor espaço de tempo possível. Sendo que apenas tínhamos 5 elementos inicialmente, criar duas aplicações nativas estava fora de questão, por isso adotamos <a href="https://flutter.dev/">Flutter</a>. Para a equipa Flutter Portugal a escolha foi fácil, por vários motivos:</p><ul><li>Flutter permite que com uma base de código sejam criadas 2 aplicações — para Android e para iOS, reduzindo para metade o tempo de execução do projecto;</li><li>Cada um de nós já tinha comprovado a robustez da tecnologia, com projectos pessoais ou pelo facto de ser a tecnologia que utilizamos no dia a dia no trabalho;</li><li>Todos somos apaixonados pela tecnologia, e com bastante frequência usamos o nosso <a href="https://discordapp.com/">Discord</a> para discutir ideias e tirar dúvidas uns com os outros;</li><li>A nossa equipa já tinha trabalhado em conjunto para o projecto Fogos.pt, no qual foi reescrita a aplicação nativa em Flutter, por isso não só já tínhamos comprovado que conseguimos entregar um projecto como equipa, como também comprovamos que Flutter era uma óptima escolha para este tipo de projecto.</li></ul><p><a href="https://play.google.com/store/apps/details?id=com.tomahock.fogos">Fogos.pt - Apps on Google Play</a></p><p>Com a conclusão deste projecto, ficam muitas lições aprendidas que gostaríamos aqui de partilhar:</p><ul><li>A urgência e escala deste projecto requereu uma comunicação rápida e eficiente, pelo que optamos por utilizar o nosso servidor Discord para o mesmo. Dividimos a comunicação em dois canais. O primeiro canal, de voz e áudio, servia para discutirmos rapidamente todas as questões do projecto e pedirmos ajuda a outros membros da equipa. O segundo canal tinha apenas anúncios importantes — novas funcionalidades, ponto da situação, o que tínhamos para fazer e novas informações do canal da VOST.</li></ul><p><a href="https://discordapp.com/">Discord - A New Way to Chat with Friends &amp; Communities</a></p><ul><li>Foi fulcral que todos os membros da equipa já se conhecessem e tivessem trabalhado juntos. Isto fez com que facilmente se tivesse dividido as tarefas de modo a que aproveitássemos sempre os pontos fortes de cada um — comunicação com outras entidades, design, preparação de uma plataforma de testes e distribuição ou organização e gestão do projecto.</li><li>O facto de termos um contacto directo com o designer <a href="http://www.ffrutuoso.com">Francisco Frutuoso </a>e de este nos ter disponibilizado o projecto em <a href="https://zeplin.io/">Zeplin</a>, facilitou e agilizou a criação de todos os ecrãs.</li></ul><ul><li><a href="https://www.ffrutuoso.com/">Francisco Frutuoso - Digital Designer</a></li><li><a href="https://scene.zeplin.io/project/5e711e6973c497b6352ed9f3">COVID19 App | Zeplin Scene</a></li></ul><ul><li>O mais importante, foi que havia um grande respeito mútuo por todos os elementos da equipa. Isto fez com que ouvíssemos e discutíssemos opiniões, mesmo que estas fossem contra o que estaríamos habituados. Juntos crescemos, aprendemos e preparamo-nos para futuros projectos.</li></ul><p>A vontade de ajudar outros nesta altura de crise e a vontade de querermos novamente trabalhar juntos foi o que juntou esta equipa. São oportunidades como estas que nos enchem de experiência que iremos aplicar no nosso emprego e na nossa vida em geral. Tal como aconteceu connosco, temos a maior certeza que isto poderá ser replicado por outras equipas de programadores e designers pelo mundo fora, desde que haja uma base de respeito mútuo e um objectivo comum.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yF0W_UiruPf4JDtb9bFwHg.jpeg" /><figcaption>A equipa Flutter Portugal</figcaption></figure><p>Por fim, deixamos uma mensagem para as Organizações Sem Fins Lucrativos e órgãos do Governo:</p><p>A equipa Flutter Portugal está aqui, mais unida que nunca.</p><p><strong>Desafiem-nos.</strong></p><p><em>Escrito por </em><a href="https://medium.com/@solid.goncalo"><em>Gonçalo Palma</em></a><em> e </em><a href="https://mochilavermelha.blogs.sapo.pt/"><em>Simone Júlio</em></a></p><p>Querem ajudar esta causa? Vão ao repositório no Github da app Estamos On e vejam quais os issues abertos!</p><p><a href="https://github.com/vostpt/covid19-mobile">vostpt/covid19-mobile</a></p><p><strong>Website Flutter Portugal:</strong></p><p><a href="https://flutter.pt">Flutter PT</a></p><p><strong>Twitter Flutter Portugal:</strong></p><p><a href="https://twitter.com/FlutterPortugal">Flutter Portugal</a></p><p><strong>Juntem-se a nós no nosso Discord! → </strong><a href="https://chat.flutter.pt">http://chat.flutter.pt</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4392796ad016" width="1" height="1" alt=""><hr><p><a href="https://medium.com/flutter-portugal/estamos-on-a-nossa-contribui%C3%A7%C3%A3o-4392796ad016">Estamos On — A nossa contribuição</a> was originally published in <a href="https://medium.com/flutter-portugal">Flutter Portugal</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Mixins e Classes de Base: Uma receita para o sucesso no Flutter]]></title>
            <link>https://medium.com/flutter-portugal/mixins-e-classes-de-base-uma-receita-para-o-sucesso-no-flutter-4b8c5d642c62?source=rss----8ea47648f583---4</link>
            <guid isPermaLink="false">https://medium.com/p/4b8c5d642c62</guid>
            <category><![CDATA[dart]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[mixin]]></category>
            <category><![CDATA[flutter]]></category>
            <dc:creator><![CDATA[Mariana Castanheira]]></dc:creator>
            <pubDate>Mon, 14 Oct 2019 08:52:36 GMT</pubDate>
            <atom:updated>2019-10-14T08:52:36.368Z</atom:updated>
            <content:encoded><![CDATA[<h3><strong><em>Mixins</em> e Classes de Base: Uma receita para o sucesso em Flutter</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*gNTeWULEfcLzuR9c.png" /><figcaption>Foto de <a href="https://unsplash.com/photos/0tfz7ZoXaWc?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Matt Briney</a> em <a href="https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unplash</a></figcaption></figure><p>Na <a href="https://twitter.com/flutterportugal">Flutter Portugal</a> acreditamos que o conhecimento tem de ser acessível a todos, independentemente do grau de conhecimento de línguas estrangeiras. Assim, vamos lançar uma pequena série de artigos que vão abrangir vários tópicos: Dados persistidos, State Management, Navegação, etc…</p><p>Este artigo foi originalmente escrito por <a href="https://medium.com/@solid.goncalo">Gonçalo Palma</a> com o título “<a href="https://medium.com/flutter-community/mixins-and-base-classes-a-recipe-for-success-in-flutter-bc3fbb5da670">Mixins and Base Classes: A recipe for success in Flutter</a>” e traduzido com autorização do autor.</p><p>Quando se desenvolve uma aplicação com múltiplos ecrãs, tendemos a reutilizar o mesmo pedaço de código em muitas classes: mostrar mensagens de erro, utilizar a mesma disposição de página e inicializar algumas dependências como, por exemplo, um <em>Bloc</em>. Tudo isto pode ser resolvido se estivermos a usar uma classe de base (<em>base class</em>) abstract, porém, e se tivermos um conjunto de características/classes que nós queremos utilizar num ecrã particular, mas não noutros? Tendo em conta que uma classe não pode ser uma filha (<em>child</em>) de mais do que uma classe, deveremos nós criar diferentes classes de base, tantas quanto o número de combinações que temos? É por isso que temos mixinss.</p><h3><strong><em>Mixins</em> e Classes de Base: Uma introdução</strong></h3><p><em>Mixins</em> deixam-nos adicionar um conjunto de “características” a uma classe, sem usar a hierarquia <em>parent-child</em>, permitindo-nos ter, na mesma classe, um pai (<em>parent</em>) e múltiplos componentes mixin. Assim, já que não é um pai da nossa classe, mixins não permitem nenhuma declaração de construtores. Podem ler mais sobre isto neste artigo de<a href="https://medium.com/flutter-community/dart-what-are-mixins-3a72344011f3"> Romain Rastel</a>, com a advertência de que o Dart 2 tem agora a palavra-chave mixin, como visto na<a href="https://dart.dev/guides/language/language-tour#adding-features-to-a-class-mixins"> documentação</a>.</p><p>Mas como é que os mixins funcionam? Vamos tomar como exemplo uma absrract class Person .</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7d3f738c795d6ace4b1cf1800ff2d37b/href">https://medium.com/media/7d3f738c795d6ace4b1cf1800ff2d37b/href</a></iframe><p>Podemos utilizar esta classe como um pai<em>, </em>usando a palavra-chave extend, assim:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7737c8cac810f30de3bc7222ff0aa665/href">https://medium.com/media/7737c8cac810f30de3bc7222ff0aa665/href</a></iframe><p>Com isto, podemos incicializar a classe e chamar o método do pai think().</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a428015f419fa3fd00cd7325443dc8b0/href">https://medium.com/media/a428015f419fa3fd00cd7325443dc8b0/href</a></iframe><p>Mas, e se quisermos adicionar novas funcionalidades ao Mike? E se o Mike for um programador e precisa de uma função codeque possa ser usada noutro Person, mas não seja utilizada por todos os Persons? mixins resolvem esse problema.</p><p>Primeiro, precisamos de criar uma classe mixine expor os novos métodos que queremos usar.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/cdb2cf99a62b3574100610727fa045b0/href">https://medium.com/media/cdb2cf99a62b3574100610727fa045b0/href</a></iframe><p>Com a palavra-chave withpodemos adicionar este “elemento” à nossa classe Mike:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a6535c54cab7444cbf16392c30fc64c6/href">https://medium.com/media/a6535c54cab7444cbf16392c30fc64c6/href</a></iframe><p>E, como com o pai, podemos chamar todas as funções que criámos no Coder.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bda93628bc616084ab5648b45599df24/href">https://medium.com/media/bda93628bc616084ab5648b45599df24/href</a></iframe><p>Agora, todas as classes que usem o mixin Coder podem efetivamente programar. No entanto, surge um problema: isto significa que, se tivermos uma classe <em>parent</em> Animalque tem uma <em>child</em> Squirrel, também podemos ter um Squirrelque consegue code()! Para prevenir esta situação, podemos “bloquear” o uso do mixinpara uma classe e todas as classes que dele herdaram a palavra-chave on:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/99b75d1ae68128131fccb81b485949e9/href">https://medium.com/media/99b75d1ae68128131fccb81b485949e9/href</a></iframe><p>Isto também nos dá uma ferramenta poderosa: agora podemos fazer <em>override</em> a métodos que estavam definidos na classe Personpara adicionar ou alterar a sua funcionalidade.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/40306d245f66ccaf62e06405faa99931/href">https://medium.com/media/40306d245f66ccaf62e06405faa99931/href</a></iframe><p>Chamar o super.think()garante que ainda estamos a chamar o código que foi definido em Person. O código acima dá-nos o seguinte <em>output</em> para o método thinkin. Mike:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/64eaa7c80dcd790ccd8376b122279600/href">https://medium.com/media/64eaa7c80dcd790ccd8376b122279600/href</a></iframe><p>Ao adquirir os conceitos de ambos base classes e mixins, agora podemos aplicá-los às nossas aplicações Flutter.</p><h3><strong>Mixins e Classes Base: Um exemplo Flutter prático</strong></h3><p>Como é que podemos aplicar isto nas nossas aplicações Flutter?</p><p>Tomemos como exemplo os dois ecrãs seguintes:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*keKl9qckMjHeZOgr.png" /></figure><p>A nossa aplicação tem vários ecrãs com a disposição mostrada acima. Em vez de copiar e colar a <em>appbar</em> (barra da aplicação) e o fundo para cada ecrã, podemos resolver o nosso problema ao utilizar mixins.</p><p>Em ambos os casos, temos um título de ecrã definido; vamos criar uma base classque tem um método para fornecer o nome do nosso ecrã, chamada BasePage. Também vamos aplicar os mixins só no StatefulWidgets, já que as nossas classes vão manter e mudar o seu estado. Com isto, nós criamos duas classes para serem usadas nas nossas páginas: uma BasePagee uma BaseState&lt;BasePage&gt;que estendem SatefulWidgete State&lt;StatefulWidget&gt;, respetivamente.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c4fa74dbe3a615c49ac28ade6fedaac2/href">https://medium.com/media/c4fa74dbe3a615c49ac28ade6fedaac2/href</a></iframe><p>Focando agora no segundo ecrã, já podemos criar o seu mixin BasicPageMixin personalizado, onde definimos o fundo e a <em>appbar</em> da nossa página.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7bd21dedc350559ba7f5cece04531b05/href">https://medium.com/media/7bd21dedc350559ba7f5cece04531b05/href</a></iframe><p>Tendo em conta que o método body()não tem um corpo (<em>body</em>), cada classe que use este mixintem de o implementar, assegurando que nós não nos esqueçamos de adicionar um corpo à nossa página.</p><p>Na captura de ecrã acima, vemos um FloatingActionButton, mas poderemos não precisar dele para todos os ecrãs, então como é que o podemos definir? Ao declarar um novo método, fab(), que por predefinição devolve um Container. Se uma classe necessita de adicionar um FloatingActionButton, eles podem substituir este método.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4782ef3b265890090982591fb0c3ca9b/href">https://medium.com/media/4782ef3b265890090982591fb0c3ca9b/href</a></iframe><p>Com o nosso mixincriado podemos agora aplicá-lo a uma nova página.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bd39276c4c3b4d5cef3be89a1b591c46/href">https://medium.com/media/bd39276c4c3b4d5cef3be89a1b591c46/href</a></iframe><p>E, assim, só nos resta declarar um body()e um possível <em>widget</em> fab()para ser usado em cada ecrã, poupando-nos umas dúzias de linhas de código.</p><h3><strong>Combinar <em>mixins</em></strong></h3><p>Sendo uma nova funcionalidade, alguns dos nossos ecrãs vão fazer chamadas a uma API (Interface de Programação de Aplicações) e, se ocorrer algum erro, precisaremos de exibir uma mensagem de erro na forma de um <a href="https://api.flutter.dev/flutter/material/SnackBar-class.html"><em>Snackbar</em></a>. Além disso, decidimos utilizar a arquitectura <a href="https://www.didierboelens.com/2018/08/reactive-programming---streams---bloc/"><em>BLoC</em></a><em>,</em> na qual precisamos de injetar um novo <em>bloc</em> de cada vez que uma página é criada. Estes dois problemas vão necessitar dos seguintes passos:</p><ul><li>Modificar a nossa BasePage, através da alteração do seu construtor com o novo parâmetro <em>bloc.</em></li><li>Modificar BaseState, ao adicionar um novo GlobalKey&lt;ScaffoldState&gt;</li><li>Criar um novo mixinque nos deixa exibir mensagens de erro enviadas pelo blocna página, usando um <em>Snackbar</em></li></ul><p>No nosso BaseBloc, estamos apenas a expôr um Sinke um Stream , de forma a transmitir mensagens de erro.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/61180a2cc9636220e45563f8bcde5e28/href">https://medium.com/media/61180a2cc9636220e45563f8bcde5e28/href</a></iframe><p>Como nós não queremos outras interações com o bloc, o nosso HomeBlocvai só estender esta classe.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/6185e097541f5862d4a704e8838b8e3a/href">https://medium.com/media/6185e097541f5862d4a704e8838b8e3a/href</a></iframe><p>Prosseguimos, ao alterar o construtor da nossa BasePagepara incluir o objeto do bloc. Isto vai forçar-nos a mudar também todas as classes que o estendem para adicionar um bloc igualmente aos seus construtores. O parâmetro blocé usado como um tipo Genérico de modo a que cada classe que o estende possa declarar o tipo correto de bloc que está a utilizar. Isto assegura que, quando o estamos a chamar na BaseState, vamos obter o tipo correto de bloc, permitindo-nos aceder aos seus métodos.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d683dd5f3e30216de016b9ec5a2440b1/href">https://medium.com/media/d683dd5f3e30216de016b9ec5a2440b1/href</a></iframe><p>Quanto ao BaseState, vamos declarar um scaffoldKeypara ser usado com o ScaffoldWidget para que possamos exibir a Snackbar.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b98ef5ef2de1c08a6f6e3517d166415a/href">https://medium.com/media/b98ef5ef2de1c08a6f6e3517d166415a/href</a></iframe><p>Tal como visto anteriormente, uma das curiosas propriedades dos <em>mixin </em>é que, se estiverem ligados a uma classe, eles podem overrideos seus métodos. Isto é útil, já que no nosso StatefulWidgetpodemos ouvir ( listen) os streamsdo blocno método initState. Assim. Para exibir as mensagens de erro, podemos criar um mixinque faz <em>override </em>ao método initStatee fornece métodos para mostrar a mensagem de erro correta numa Snackbar.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f07005422484f00856d0dbe4adc6d635/href">https://medium.com/media/f07005422484f00856d0dbe4adc6d635/href</a></iframe><p>Finalmente, podemos adicioná-lo à nossa classe HomePageao adicioná-lo depois do mixin BasicPage.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8afcaf06a6b81fad14ff8f8233592bbc/href">https://medium.com/media/8afcaf06a6b81fad14ff8f8233592bbc/href</a></iframe><h3><strong>Conclusão</strong></h3><p>E lá vamos nós! ✌️ Agora podemos utilizar ambos, mixins e abstract classes, para reutilizar o código nas nossas aplicação.</p><p>Talvez não precisemos de fazer uma base UI (Interface de Utilizador) para a nossa aplicação, mas podemos usar mixins como ErrorHandlingMixinpara mostrar uma mensagem de erro ao utilizador, um feedback quando estivermos a carregar uma página ou até mostrar um ecrã com “A Aplicação está <em>Offline</em>”.</p><p>No entanto, criar ambas as classes base e os mixins é um processo que precisa de alguma deliberação, caso contrário poderemos estar perante um “<em>Deadly Diamond of Death</em>”, em que quando a chamar um método que é declarado em ambos os mixins e classes base, o compilador não sabe qual escolher. Pode ler mais sobre isto no <a href="https://medium.com/flutter-community/https-medium-com-shubhamhackzz-dart-for-flutter-mixins-in-dart-f8bb10a3d341">artigo de Shubham Soni</a> sobre mixins em Flutter.</p><p>Finalmente, e tal como sugerido por <a href="https://medium.com/@remirousselet">Remi Rousselet</a>, devemos estar atentos a como o uso extensivo de mixins pode ser considerado anti-padrões, podendo-se ler mais <a href="https://reactjs.org/blog/2016/07/13/mixins-considered-harmful.html">neste artigo</a> de <a href="https://twitter.com/dan_abramov">Dan Abramov</a>.</p><ul><li><a href="https://gpalma.pt">Blog</a></li><li><a href="https://medium.com/flutter-community/mixins-and-base-classes-a-recipe-for-success-in-flutter-bc3fbb5da670">Mixins and Base Classes: A recipe for success in Flutter</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4b8c5d642c62" width="1" height="1" alt=""><hr><p><a href="https://medium.com/flutter-portugal/mixins-e-classes-de-base-uma-receita-para-o-sucesso-no-flutter-4b8c5d642c62">Mixins e Classes de Base: Uma receita para o sucesso no Flutter</a> was originally published in <a href="https://medium.com/flutter-portugal">Flutter Portugal</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Flutter: Push, Pop, Push]]></title>
            <link>https://medium.com/flutter-portugal/flutter-push-pop-push-8cc3b038f415?source=rss----8ea47648f583---4</link>
            <guid isPermaLink="false">https://medium.com/p/8cc3b038f415</guid>
            <category><![CDATA[dart]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[Mariana Castanheira]]></dc:creator>
            <pubDate>Tue, 24 Sep 2019 05:32:42 GMT</pubDate>
            <atom:updated>2019-09-24T05:32:42.183Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*iVeQlKlHs7dbEAEB.jpeg" /></figure><p>Na <a href="https://twitter.com/flutterportugal">Flutter Portugal</a> acreditamos que o conhecimento tem de ser acessível a todos, independentemente do grau de conhecimento de línguas estrangeiras. Assim, vamos lançar uma pequena série de artigos que vão abrangir vários tópicos: Dados persistidos, State Management, Navegação, etc…</p><p>Este artigo foi originalmente escrito por <a href="https://medium.com/@poojabhaumik">Pooja Bhaumik</a> com o título “<a href="https://medium.com/flutter-community/flutter-push-pop-push-1bb718b13c31">Flutter: Push, Pop, Push</a>” e traduzido com autorização da autora.</p><p>Construir uma UI (Interface do Utilizador) é muito simples com todos os widgets que a <em>framework </em>disponibiliza, parte da qual abordei no meu <a href="https://medium.com/flutter-community/breaking-layouts-in-rows-and-columns-in-flutter-8ea1ce4c1316">último artigo.</a> Mas não podemos ter somente uma aplicação linda que não faz nada funcional. Ser-nos-à pedido que naveguemos pela aplicação ou que enviemos dados para a frente e para trás entre ecrãs. Em Flutter, a navegação de um ecrã para outro é possível devido aos <a href="https://api.flutter.dev/flutter/widgets/Navigator-class.html"><strong><em>Navigators</em></strong></a><strong><em>, </em></strong>um widget simples que mantém uma <em>stack </em>(pilha)<strong><em> </em></strong>de <strong><em>Routes </em></strong>(rotas)<strong><em>, </em></strong>ou em termos mais simples, um histórico de páginas/ecrãs visitados.</p><p>Encontraremos imensos artigos que dizem como <em>push </em>(empurrar)para um novo ecrã, ou <em>pop </em>(remover) do ecrã atual, mas este artigo é um pouco mais do que isso. Este irá focar-se mais na maioria dos métodos do <em>Navigator</em> e descrever um caso prático para cada um deles.</p><h3><strong>Antes de começarmos…</strong></h3><p><em>Mencionou </em>Routes<em> algures, o que é isso?</em></p><p><em>Routes</em> é uma abstração para um ecrã ou página de uma aplicação. Por exemplo, &#39;/home&#39;vai levar-nos ao <em>HomeScreen </em>(Ecrã Inicial) ou &#39;/login&#39;vai levar-nos ao<em> LoginScreen</em> (Ecrã de Login). &#39;/&#39;vai ser a nossa rota inicial. Isto pode soar parecido ao <em>Routing </em>no desenvolvimento REST API. Então &#39;/&#39;pode atuar como uma <em>root</em> (raiz).</p><p>Esta é a forma como iríamos declarar as nossas rotas na nossa aplicação Flutter.</p><pre><strong>new</strong> MaterialApp(<br>  home: <strong>new</strong> Screen(),<br>  routes: &lt;String, WidgetBuilder&gt; {<br>    &#39;/screen1&#39;: (BuilderContext context) =&gt; <strong>new</strong> Screen1(),<br>    &#39;/screen2&#39; : (BuilderContext context) =&gt; <strong>new</strong> Screen2(),<br>    &#39;/screen3&#39; : (BuilderContext context) =&gt; <strong>new</strong> Screen3(),<br>    &#39;/screen4&#39; : (BuilderContext context) =&gt; <strong>new</strong> Screen4()<br>  },<br>)</pre><p><em>Screen1(), Screen2(), etc são os nomes das classes para cada ecrã.</em></p><h3><strong><em>Push, push, push.</em></strong></h3><p>Se tem algum conhecimento sobre Data Structures (Estruturas de Dados), então sabe sobre <strong><em>Stacks</em></strong>. Se tem conhecimentos, ainda que básicos, sobre <em>stacks</em>, então sabe sobre <em>push</em> e <em>pop</em>.</p><p>Se não sabe, <em>pushing</em> é adicionar um elemento ao topo da <em>stack</em> de elementos e <em>popping</em> é remover o elemento no topo da mesma <em>stack</em>.</p><p>No caso do Flutter, quando navegamos para outro ecrã, usamos o método pushe o widget Navigatoradiciona o novo ecrã ao topo da <em>stack</em>. Naturalmente, o método popiria remover esse ecrã da <em>stack</em>.</p><p>Avancemos então para a base de código do nosso <a href="https://github.com/PoojaB26/NavigatorsDemo-Flutter">projeto-amostra</a> e vejamos como é que podemos movimentar-nos do Ecrã 1 para o Ecrã 2. <em>Podemos experimentar com os métodos ao correr a aplicação-amostra.</em></p><pre>new RaisedButton(<br>   onPressed:(){<br>   <strong>Navigator.<em>of</em>(context).pushNamed(&#39;/screen2&#39;);</strong><br>},<br>   child: new Text(&quot;Push to Screen 2&quot;),<br>),</pre><p><em>Isto foi curto.</em></p><p>Lá isso foi. Com a ajuda dos métodos pushNamed, podemos navegar para qualquer ecrã cuja rota é definida em main.dart. Chamamo-lhes namedRoutepara referência. O caso prático deste método é bastante direto. Para simplesmente navegar.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/94/0*3gcIZxjHieSkiqgK.png" /><figcaption>Stack depois de fazer push no Screen2</figcaption></figure><h3><strong><em>Pop</em> it</strong></h3><p>Agora, quando nos queremos ver livres do último ecrã visitado, que é o Screen2neste caso, precisaríamos de remover <em>Routes</em> da<em> stack</em> do <em>Navigator</em> usando o método pop.</p><pre>Navigator.<em>of</em>(context).pop();</pre><p><em>Não esquecer que esta linha de código vai por dentro do método </em><em>OnPressed.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/93/0*7TnlgtqgeKfpmLMi.png" /><figcaption>Stack depois de fazer pop ao Screen2</figcaption></figure><p>Quando se usa uma Scaffold, normalmente não é necessário fazer <em>pop</em> da rota explicitamente, porque o Scaffold automaticamente adiciona um botão “<em>back</em>” (voltar) na sua AppBar, o qual irá chamar o Navigator.pop()quando pressionado. Mesmo em Android, pressionar o botão “<em>back</em>” do dispositivo faria o mesmo. Mas mesmo assim, poderemos precisar deste método para outros casos práticos, tais como <em>pop </em>de<em> </em>um AlertDialog quando o utilizador clica no botão de Cancelar.</p><p><strong>Porquê <em>pop</em> em vez de fazer <em>push</em> de volta ao ecrã anterior?</strong></p><p>Imaginemos que temos uma aplicação de Reserva de Hotel que lista os hotéis na localização que desejamos. Clicar numa das listas vai levar-nos a um ecrã que contém mais detalhes sobre o hotel. Escolhemos um, mas odiamos o hotel e queremos voltar à lista. Se fizermos pushde volta ao HotelListScreen, vamos estar também a manter o nosso DetailsScreenna nossa <em>stack</em>. Assim, clicar no botão de voltar atrás levar-nos-ia de novo ao DetailsScreen. <em>Tão confuso!</em></p><p>Em vez disso devemos testá-lo. Correr a aplicação-amostra, ver a appBar no nosso Screen1, não tem nenhum botão para voltar atrás, porque é a <em>Route</em> inicial (ou ecrã inicial), agora clicamos em <strong><em>Push to Screen 2</em></strong> e, em vez do botão de retornar, carregar no <strong><em>Push to Screen 1</em></strong> <strong>em vez de</strong> <strong><em>Pop</em></strong> e, agora, ver a appBar no Screen1.</p><p>Carregar no recente botão de voltar atrás vai fazer-nos recuar ao Screen2e nós não queremos isso neste tipo de casos.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/92/0*GGIBTovWzwdLaTn5.png" /><figcaption>Esta vai ser a Stack numa situação do tipo</figcaption></figure><h4><strong><em>maybePop</em></strong></h4><p>E se estivermos na <em>Route</em> inicial e alguém, por engano, tentou fazer <em>pop</em> a este ecrã. <em>Popping</em> o único ecrã na <em>stack</em> iria fechar a nossa aplicação, porque assim não teria nenhuma rota para exibir. Nós definitivamente não queremos que o utilizador tenha uma experiência tão inesperada. É aí que o maybePop()aparece. Por isso, fica do género “faça pop só se conseguir”. Experimente, clique no botão maybePopno Screen1e não vai fazer nada. <em>Porque não há nada para fazer </em>pop<em>.</em> Agora tente o mesmo no Screen3e irá fazer <em>pop</em> ao ecrã. <em>Porque pode.</em></p><h4><strong><em>canPop</em></strong></h4><p><em>É espantoso conseguirmos fazer isto, mas como é que posso saber se isto é a </em>route<em> inicial? Seria bom se pudesse exibir algum alerta para o utilizador em casos destes.</em></p><p>Ótima questão, é só chamar este método canPop()e regressará truese esta <em>route</em> puder ser <em>popped</em> e falsese não for possível.</p><p><em>Esperimente ambos os métodos </em><em>canPope </em><em>maybePopno </em><em>Screen1e </em><em>Screen3, e veja a diferença. Os print values para o </em><em>canPopvão mostrar no separador da sua consola/terminal do seu IDE (Ambiente de Desenvolvimento Integrado).</em></p><h3><strong><em>Push </em>um pouco mais</strong></h3><p>Voltemos a mais métodos de <em>push</em>. Agora estamos a aprofundar. Vamos falar sobre substituir uma rota por uma nova rota. Temos dois métodos que podem fazer isto — pushReplacementNamed e popAndPushNamed.</p><pre><strong>Navigator.<em>of</em>(context).pushReplacementNamed(&#39;/screen4&#39;);<br>                       </strong>//and<br><strong>Navigator.popAndPushNamed(context, &#39;/screen4&#39;);</strong></pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/242/0*ZG06SBM4nu5XlUNo.png" /><figcaption>Stack antes e depois</figcaption></figure><p>Tente experimentar com ambos no Screen3da aplicação-amostra. E repare na animação de saída e entrada em cada caso. pushReplacementNamedvai executar a animação de entrada e popAndPushNamedvai executar a animação de saída. Podemos utilizar isto para os próximos casos práticos possíveis.</p><h4><strong>Caso prático: <em>pushReplacementNamed</em></strong></h4><p>Quando o utilizador iniciou sessão com sucesso, e agora poderá estar no DashboardScreen, não queremos que o utilizador regresse ao LoginScreenem caso algum. Então, a rota de <em>login</em> deveria estar completamente substituída pela rota do painel. Outro exemplo seria ir ao HomeScreena partir do SplashScreen. Só deverá ser mostrado uma vez e o utilizador não deveria ser capaz de regressar a ele novamente a partir do HomeScreen. Em casos destes, uma vez que vamos passar para um ecrã completamente novo, poderemos querer utilizar este método para a sua propriedade de animação de entrada.</p><h4><strong>Caso prático: <em>popAndPushNamed</em></strong></h4><p>Suponhamos que estamos a construir uma aplicação de Compras que mostra uma lista de produtos no seu ProductsListScreene o utilizador pode aplicar filtros no FiltersScreen. Quando o utilizador clica no botão Apply Changes, o FiltersScreen deveria fazer <em>pop</em> e<em> push</em> de volta ao ProductsListScreencom os novos valores do filtro. Aqui, a propriedade de animação de saída do popAndPushNamedseria mais apropriada.</p><h3><strong>Até ao fim…</strong></h3><p>Estamos quase no fim do artigo. Bem, quase.</p><p>Nesta secção vamos abordar os próximos três métodos pushNameAndRemoveUntile popUntil.</p><h4><strong>Caso prático: <em>pushNamedAndRemoveUntil</em></strong></h4><p>Então, basicamente estamos a construir uma aplicação do género do Facebook/Instagram, em que o utilizador inicia sessão, percorre o seu <em>feed</em>, persegue perfis diferentes e, quando acaba, quer terminar a sessão na aplicação. Depois de terminar sessão, não podemos simplesmente fazer pushnum HomeScreen(ou em qualquer ecrã que precise de ser exibido depois de terminar a sessão) em casos deste tipo. Queremos remover todas as rotas na <em>stack</em> para que o utilizador não consiga regressar às rotas anteriores depois de ter terminado a sua sessão.</p><pre>Navigator.<em>of</em>(context).pushNamedAndRemoveUntil(&#39;/screen4&#39;, (Route&lt;<strong>dynamic</strong>&gt; route) =&gt; <strong>false</strong>);</pre><p>Aqui, (Route&lt;dynamic&gt; route =&gt; falsevai certificar-se de que todas as rotas anteriores à rota que foi <em>pushed </em>são removidas.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/241/0*-tMKNSN1lmVOdABz.png" /><figcaption>Terminar sessão remove todas as routes e leva o utilizador de volta ao LoginScreen</figcaption></figure><p>Agora, em vez de remover todas as rotas antes das rotas <em>pushed, </em>só podemos remover um certo número de rotas. Tomemos como exemplo outra aplicação de Compras! Ou basicamente qualquer aplicação que exija uma transação de pagamento.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/251/0*evV8da406ZU13xn3.png" /></figure><p>Nestas aplicações, uma vez que o utilizador tenha completado a transação de pagamento, todos os ecrãs relacionados com a transação ou com o carrinho devem ser removidos da <em>stack</em> e o utilizador deve ser levado ao PaymentConfirmationScreen. Clicar no botão de retroceder deverá levá-los de volta somente ao ProductsListScreenou HomeScreen.</p><pre>Navigator.<em>of</em>(context).pushNamedAndRemoveUntil(&#39;/screen4&#39;, ModalRoute.<em>withName</em>(&#39;/screen1&#39;));</pre><p>De acordo com o fragmento do código, fazemos push ao Screen4e removemos todas as rotas até ao Screen1para que a nossa <em>stack</em> se pareça com isto.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/213/0*JSHPW64NoJKvyKs6.png" /><figcaption>Antes e depois</figcaption></figure><h4><strong>Caso prático: <em>popUntil</em></strong></h4><p>Imaginemos que estamos a construir uma aplicação tipo Formulários Google ou uma aplicação que nos deixa preencher e organizar formulários Google. Um utilizador poderá ter de preencher um longo formulário de 3 partes, o qual poderá ser exibido em 3 ecrãs sequenciais numa aplicação móvel. Na 3ª parte do formulário, o utilizador decide cancelar o preenchimento do formulário. O utilizador clica em Cancele todos os ecrãs anteriores relacionados com o formulário devem ser <em>popped</em> e o utilizador deve ser levado de volta ao HomeScreenou DashboardScreen,perdendo, assim, todos os dados relacionados com o formulário (que é o que desejamos neste tipo de casos). Não estaremos a fazer <em>push</em> em nada de novo aqui, só a retornar a uma rota anterior.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/249/0*CM2YK8cGOFKrSclC.png" /><figcaption>É feito pop em todos os ecrãs relacionados com Form</figcaption></figure><pre>Navigator.<em>popUntil</em>(context, ModalRoute.<em>withName</em>(&#39;/screen2&#39;));</pre><h3><strong>Onde estão os dados?</strong></h3><p>Na maioria dos exemplos anteriores, estou apenas a fazer <em>push</em> numa nova rota sem enviar dados nenhuns, mas um cenário assim é muito pouco provável numa aplicação real. Para enviar dados, estaríamos a utilizar o <em>Navigator</em> para fazer <em>push</em> numa nova MaterialPageRoute para a <em>stack</em> com os nossos dados, (aqui é userName).</p><pre>String userName = &quot;John Doe&quot;;<br>Navigator.<em>push</em>(<br>    context,<br>    <strong>new </strong>MaterialPageRoute(<br>        builder: (BuildContext context) =&gt;<br>        <strong>new </strong>Screen5(userName)));</pre><p>Para recuperar os valores no Screen5, adicionaríamos um construtor parametrizado no Screen5, desta forma:</p><pre><strong>class </strong>Screen5 <strong>extends </strong>StatelessWidget {<br><br>  <strong>final String userName;<br>  Screen5(this.userName);</strong><br>  @override<br>  Widget build(BuildContext context) {<br><strong>  print(userName)<br></strong>  ...<br>  }<br>}</pre><p>Isto significa que não só podemos usar o método MaterialPageRoutepara push,mas também para pushReplacement, pushAndPopUntil, etc. Basicamente, introduza o termo-chave nameda partir dos métodos acima descritos e o primeiro parâmetro irá agora tomar MaterialPageRouteem vez de um Stringda <em>namedRoute</em>.</p><h4><strong>Devolve-me alguns dados, meu</strong></h4><p>Poderemos também querer devolver dados a partir de um novo ecrã. Suponhamos que estamos a construir uma aplicação de Alarme e, para definir um novo toque para o nosso alarme, iríamos exibir uma lista de opções de áudio. Obviamente, iríamos precisar do item de dados selecionado quando a caixa de Diálogo tiver sido <em>popped</em>. Pode ser conseguido assim:</p><pre><strong>new </strong>RaisedButton(onPressed: ()<strong>async</strong>{<br> <strong> String value = await Navigator.<em>push</em>(context, new MaterialPageRoute&lt;String&gt;</strong>(<br>      builder: (BuildContext context) {<br>        <strong>return new </strong>Center(<br>          child: <strong>new </strong>GestureDetector(<br>              child: <strong>new </strong>Text(&#39;OK&#39;),<br>              onTap: () { <strong>Navigator.<em>pop</em>(context, &quot;Audio1&quot;);</strong> }<br>          ),<br>        );<br>      }<br>  )<br>  );<br>  print(value);<br><br>},<br>  child: <strong>new </strong>Text(&quot;Return&quot;),)</pre><p><em>Experimente no </em><em>Screen4e verifique a consola para os print values.</em></p><p>Também é de notar: Quando uma rota é usada para recuperar um valor, o tipo de parâmetro da rota deve corresponder ao tipo de resultados do <em>pop.</em> Aqui, necessitamos de dados de <em>String</em> (corda), por isso usámos MaterialPageRoute&lt;String&gt;. Também não faz mal se não o tipo não for especificado.</p><h3><strong>Uau, isso foi muita informação</strong></h3><p>De facto, foi, e eu podia ter explicado somente os métodos e a sua implementação, mas visto que há tantos métodos <em>Navigator</em>, eu queria explicá-los usando cenários trazidos de aplicações do mundo real. Espero que isto vos tenha ajudado a alargar o vosso horizonte <em>Navigator</em>.</p><ul><li><a href="https://medium.com/flutter-community/flutter-push-pop-push-1bb718b13c31">Flutter: Push, Pop, Push</a></li><li><a href="https://medium.com/@poojabhaumik">Pooja Bhaumik - Medium</a></li><li><a href="https://twitter.com/FlutterPortugal">Flutter Portugal</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8cc3b038f415" width="1" height="1" alt=""><hr><p><a href="https://medium.com/flutter-portugal/flutter-push-pop-push-8cc3b038f415">Flutter: Push, Pop, Push</a> was originally published in <a href="https://medium.com/flutter-portugal">Flutter Portugal</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Como usar SQLite em Flutter]]></title>
            <link>https://medium.com/flutter-portugal/como-usar-sqlite-em-flutter-fdd8bcfa6155?source=rss----8ea47648f583---4</link>
            <guid isPermaLink="false">https://medium.com/p/fdd8bcfa6155</guid>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[sqlite]]></category>
            <category><![CDATA[dartlang]]></category>
            <dc:creator><![CDATA[Mariana Castanheira]]></dc:creator>
            <pubDate>Mon, 16 Sep 2019 09:30:45 GMT</pubDate>
            <atom:updated>2019-09-24T05:23:21.931Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*utO_gPCYrBw-BTkwy3VJJg.png" /></figure><p>Na <a href="https://twitter.com/flutterportugal">Flutter Portugal</a> acreditamos que o conhecimento tem de ser acessível a todos, independentemente do grau de conhecimento de línguas estrangeiras. Assim, vamos lançar uma pequena série de artigos que vão abrangir vários tópicos: Dados persistidos, State Management, Navegação, etc…</p><p>Este artigo foi originalmente escrito por <a href="https://medium.com/@rahiche?source=post_page-----187c1a82e8b----------------------">Raouf Rahiche</a> com o título “<a href="https://medium.com/flutter-community/using-sqlite-in-flutter-187c1a82e8b">Using SQLite in Flutter</a>” e traduzido com autorização do autor.</p><p>Dados persistidos (<em>Persitent Data</em>) são muito importantes para os utilizadores, já que lhes seria inconveniente estar sempre a escrever a sua informação ou esperar que a rede carregue os mesmos dados novamente. Nestas situações, o melhor seria guardar os seus dados localmente.</p><p>Neste artigo, vou demonstrá-lo, usando SQLiteem FLutter.</p><h3><strong>Porquê SQLite?</strong></h3><p>SQLite é um dos métodos mais populares para armazenar dados localmente. Para este artigo, vamos usar o pacote (<em>package</em>) sqflitepara acedermos ao SQLite. <a href="https://pub.dev/packages/sqflite"><em>Sqflite</em></a><em> </em>é um dos pacotes mais utilizados e atualizados para ligar às bases de dados SQLite no Flutter.</p><h3><strong>1. Adicionar a dependência ao projeto</strong></h3><p>No nosso projeto, vamos abrir o ficheiropubspec.yamle procuramos por dependencies. Por baixo de dependencies, adicionamos a última versão do sqflitee path_provider(que podem ser retiradas do <a href="https://pub.dev/packages/sqflite/versions#-versions-tab-">pub.dev</a>).</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9a562670fefaf9ce290dab7b636eba6f/href">https://medium.com/media/9a562670fefaf9ce290dab7b636eba6f/href</a></iframe><p><em>NOTA</em>: Nós usamos o pacote path_providerpara obter o directório comum, como o TemporaryDirectorye ApplicationDocumentsDirectory.</p><h3>2. Criação de um <em>DB Client</em></h3><p>Agora, no nosso projeto, vamos criar um novo ficheiro Database.dart. Neste ficheiro, vamos precisar de criar um <em>singleton.</em></p><blockquote><strong>Porque é que precisamos de um <em>singleton</em>:</strong> usamos o padrão <em>singleton</em> para assegurar que temos apenas uma <em>class instance</em> (instância de classe) e que providenciamos um ponto de acesso global a ele.</blockquote><p>1- Criação de um construtor privado que possa ser usado apenas dentro da classe:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/30e9f60857a74529022c7bbabe7e8cde/href">https://medium.com/media/30e9f60857a74529022c7bbabe7e8cde/href</a></iframe><p>2- Preparação da base de dados</p><p>A seguir vamos criar o objeto da base de dados e fornecer-lhe um <em>getter, </em>que irá criar uma instância da base de dados, se já não tiver sido criada (<em>lazy initialization, </em>inicialização diferida).</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2da48534800cb93a8176e15c98dba151/href">https://medium.com/media/2da48534800cb93a8176e15c98dba151/href</a></iframe><p>Se não houver nenhum objeto atribuído à base de dados, usamos a função initDB para criar a base de dados. Nesta função, iremos obter o directório onde iremos armazenar a base de dados e criar as tabelas que desejamos:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ffc83e67df06fc9dbfa7ea5e85bf2b1e/href">https://medium.com/media/ffc83e67df06fc9dbfa7ea5e85bf2b1e/href</a></iframe><blockquote><strong>NOTA:</strong> O nome da base de dados é TestDBe a única tabela que temos chama-se Client. Se não sabe o que é que está a acontecer, precisa mesmo de ir aprender sobre <em>SQL</em> (<em>Structured Query Language</em>, ou em português, Linguagem de Consulta Estruturada); é mais importante do que água.</blockquote><h3><strong>3. Criação da <em>Model Class </em>(Classe Modelo)</strong></h3><p>Os dados dentro da nossa base de dados vão ser convertidos em <em>Dart Maps</em>, por isso, primeiro precisamos de criar as <em>Model Classes</em> com os métodos toMape fromMap. Não vou abordar como é que isto se faz manualmente, para isso poderemos ler este <a href="https://medium.com/flutter-community/parsing-complex-json-in-flutter-747c46655f51">artigo</a> de <a href="https://medium.com/u/10c1e6d8c265?source=post_page-----187c1a82e8b----------------------">Poojã Bhaumik</a>.</p><p>Para criar as nossas <em>model classes</em>, vamos usar este <a href="https://app.quicktype.io/#l=dart">website</a>. Se ainda não o tem guardado como favorito, deveria mesmo fazê-lo :)</p><p><a href="https://app.quicktype.io/?share=4Ik8Upww0mN33e2CBVmq">Pode clicar aqui para ver como tudo funciona</a>.</p><p>O nosso modelo:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9cf5936d32a42debeca91e6b18c8759d/href">https://medium.com/media/9cf5936d32a42debeca91e6b18c8759d/href</a></iframe><h3><strong>4. Operações <em>CRUD</em></strong></h3><h3><strong>Criar (<em>Create</em>)</strong></h3><p>O pacote SQFLitefaculta duas maneiras de lidar com estas operações, usando consultas (<em>queries</em>) RawSQLou o nome da tabela e um mapa que contenha os dados:</p><p>Usando rawInsert :</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5240d35c9bdbebbc9ccd4c2088367696/href">https://medium.com/media/5240d35c9bdbebbc9ccd4c2088367696/href</a></iframe><p>Usando insert:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4916c0cebdac5541412bb41172a88883/href">https://medium.com/media/4916c0cebdac5541412bb41172a88883/href</a></iframe><p>Outro exemplo usando o maior ID (identificador) como um novo ID:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0bb78fd77347c588d5c95feabb6632c3/href">https://medium.com/media/0bb78fd77347c588d5c95feabb6632c3/href</a></iframe><h3><strong>Ler (<em>Read</em>)</strong></h3><h4><strong>Obter Client através de um ID</strong></h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/446891d97b8452f848fd869430606959/href">https://medium.com/media/446891d97b8452f848fd869430606959/href</a></iframe><p>No código acima, nós fornecemos a consulta com um idcomo argumento usando o whereArgs. Depois fazemos<em> return </em>ao primeiro resultado se a lista não estiver vazia, caso contrário retornamos <em>null</em>.</p><h4><strong>Obter todos os <em>Clients </em>com uma condição</strong></h4><p>Neste exemplo, usamos o rawQuerye mapeamos a lista de resultados para uma lista de objetos do Client:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/80f5fccae32334045eb18d7c3af31763/href">https://medium.com/media/80f5fccae32334045eb18d7c3af31763/href</a></iframe><p>Exemplo: Apenas obter os <em>Blocked Clients</em></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/95731ec2136723ac722fa53a3c75d3c6/href">https://medium.com/media/95731ec2136723ac722fa53a3c75d3c6/href</a></iframe><h3><strong>Atualizar (<em>Update</em>)</strong></h3><h4><strong>Atualizar um <em>Client</em> já existente</strong></h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/20e780b9336ce9fdd7920e7049766f5c/href">https://medium.com/media/20e780b9336ce9fdd7920e7049766f5c/href</a></iframe><p>Exemplo: Bloquear (<em>block</em>) ou desbloquear (<em>unblock)</em> um <em>Client.</em></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9849db40f7280a3fda171e2bfab0cad8/href">https://medium.com/media/9849db40f7280a3fda171e2bfab0cad8/href</a></iframe><h3><strong>Apagar (<em>Delete</em>)</strong></h3><h4><strong>Apagar um <em>Client</em></strong></h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a5bde015caf1468b655669e3c1b9b6d8/href">https://medium.com/media/a5bde015caf1468b655669e3c1b9b6d8/href</a></iframe><h4><strong>Apagar todos os <em>Clients</em></strong></h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e49ac5583428b83edee5beb741fb3e97/href">https://medium.com/media/e49ac5583428b83edee5beb741fb3e97/href</a></iframe><h3><strong>Demonstração</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*n5NjmYcFXfHRI4XR" /></figure><p>Para a nossa demonstração vamos criar uma aplicação Flutter simples para interagir com a nossa base de dados.</p><p>Vamos começar com a interface (<em>layout</em>) da aplicação:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/dd199360c8f9540fd6182a225e017aad/href">https://medium.com/media/dd199360c8f9540fd6182a225e017aad/href</a></iframe><p>Notas:</p><p>1- O FlutterBuilderé usado para obter dados da base de dados.</p><p>2- O FAB adiciona um cliente aleatório à base de dados quando é clicado.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/95dcdd6e9b66d30423bfe688e40d82d7/href">https://medium.com/media/95dcdd6e9b66d30423bfe688e40d82d7/href</a></iframe><p>3- É mostrado um CircularProgressIndicatorse não existirem dados.</p><p>4- Quando o utilizador clicar na <em>checkbox</em>, o cliente será bloqueado ou desbloqueado, de acordo com o estado actual.</p><p>Agora é bastante fácil adicionar novas funcionalidades, como por exemplo, se quisermos eliminar um cliente quando o item é arrastado (<em>swiped</em>), simplesmente envolvemos (<em>wrap</em>) um widget ListTilecom um widget Dismissibledesta forma:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bb297b9a7a6fb6764792d8c1a2a97691/href">https://medium.com/media/bb297b9a7a6fb6764792d8c1a2a97691/href</a></iframe><p>Para a nossa função OnDismissedvamos usar o fornecedor (<em>provider</em>) da base de dados para chamar o método deleteClient. Como argumento, vamos usar o ID do item.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*L5camabc8066dzHU" /></figure><h3><strong>Refatoração (<em>Refactoring</em>) para usar o padrão <em>BLoC</em></strong></h3><p>Fizémos imenso neste artigo, mas numa aplicação no mundo real, manter o estado (<em>state</em>) junto com a UI (<em>User Interface</em>) não é propriamente uma coisa boa. Pelo contrário, devemos sempre mantê-los afastados.</p><p>Existem muitos padrões para gerir o estado no Flutter, mas vamos utilizar o BLoC neste artigo por ser muito flexivel.</p><h4><strong>Criação do BLoC:</strong></h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9a0f45de47347c42e385bb7ce9f048db/href">https://medium.com/media/9a0f45de47347c42e385bb7ce9f048db/href</a></iframe><p>Notas:</p><p>1- getClientsirá obter os dados a partir da base de dados (tabela <em>Client</em>) assincronamente.Vamos chamar este método sempre que atualizarmos a tabela, daí o motivo para o colocar dentro do corpo do construtor.</p><p>2- Utilizamos StreamController&lt;T&gt;.broadcastno construtor para que nos seja possível ouvir (<em>listen)</em> o <em>stream</em> mais do que uma vez. No nosso exemplo não faz grande diferença, visto que só estamos a escutar o <em>stream </em>uma vez, mas é bom considerar casos em que queiramos ouvir o <em>stream</em> mais do que uma vez.</p><p>3- Não nos podemos esquecera de fechar o <em>stream</em>. Isto previne que tenhamos derrames de memória (<em>memory leaks</em>). No nosso exemplo, vamos fechá-lo usando o método <em>dispose </em>do nosso StatefulWidget.</p><p>Agora, vamos adicionar alguns métodos ao nosso BLoC para interagir com a base de dados:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2c778fc3b70f5c79fa66c7a2be67c452/href">https://medium.com/media/2c778fc3b70f5c79fa66c7a2be67c452/href</a></iframe><p>E isto é tudo quanto ao nosso BLoC!</p><p>O nosso próximo passo seria encontrar uma forma de disponibilizar o nosso <em>bloc</em> aos nossos widgets. Precisamos de uma maneira de tornar o <em>bloc </em>acessível a partir de diferentes partes da árvore (<em>tree</em>), de modo a que o consigamos libertar da memória quando não está a ser usado.</p><p>Para tal, podemos dar uma vista de olhos a esta <a href="https://github.com/rrousselGit/provider">biblioteca</a>, por <a href="https://medium.com/u/c0530952c459?source=post_page-----187c1a82e8b----------------------">Remi Rousselet</a><strong>.</strong></p><p>No nosso caso, o <em>bloc</em> só irá ser usado por um widget de forma a que o possamos declarar e dispor dele do nosso <em>stateful</em> widget.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2e39a79e43077799b2448660861029dd/href">https://medium.com/media/2e39a79e43077799b2448660861029dd/href</a></iframe><p>A seguir, precisamos de usar um StreamBuilder, em vez do FutureBuilder. Isto porque agora estamos a escutar o <em>stream</em> (<em>stream </em>dos clients) em vez de um <em>future.</em></p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7b8c059ac1fcb93edc97e5682696af8e/href">https://medium.com/media/7b8c059ac1fcb93edc97e5682696af8e/href</a></iframe><p>O último passo seria refatorar (<em>refactor</em>) o nosso código para que chamássemos os nossos métodos a partir do nosso <em>bloc</em> e não diretamente da base de dados:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/45db8d7674b9f956ca0e7eddfab9e1ef/href">https://medium.com/media/45db8d7674b9f956ca0e7eddfab9e1ef/href</a></iframe><p>Aqui está o resultado final:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/0*lwkA-uTGp8NFGox5" /></figure><p>Finalmente, pode encontrar a origem do código para este exemplo neste <a href="https://github.com/Rahiche/sqlite_demo">repo</a> (veja o setor sqlite_demo_bloc para ver a nova versão após refatorar). Espero que tenham gostado deste artigo.</p><ul><li><a href="https://medium.com/flutter-community/using-sqlite-in-flutter-187c1a82e8b">Using SQLite in Flutter</a></li><li><a href="https://medium.com/@rahiche">Raouf Rahiche - Medium</a></li><li><a href="https://twitter.com/FlutterPortugal">Flutter Portugal</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fdd8bcfa6155" width="1" height="1" alt=""><hr><p><a href="https://medium.com/flutter-portugal/como-usar-sqlite-em-flutter-fdd8bcfa6155">Como usar SQLite em Flutter</a> was originally published in <a href="https://medium.com/flutter-portugal">Flutter Portugal</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[O Flutter e a Linha de Comandos — uma história de amor]]></title>
            <link>https://medium.com/flutter-portugal/o-flutter-e-a-linha-de-comandos-uma-hist%C3%B3ria-de-amor-3f18e6c97136?source=rss----8ea47648f583---4</link>
            <guid isPermaLink="false">https://medium.com/p/3f18e6c97136</guid>
            <category><![CDATA[dart]]></category>
            <category><![CDATA[cli]]></category>
            <category><![CDATA[flutter]]></category>
            <dc:creator><![CDATA[Mariana Castanheira]]></dc:creator>
            <pubDate>Thu, 29 Aug 2019 10:56:09 GMT</pubDate>
            <atom:updated>2019-09-24T05:24:18.664Z</atom:updated>
            <content:encoded><![CDATA[<h3>O Flutter e a Linha de Comandos — uma história de amor</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ti6TFRUWfr8mvDaQk1cO8w.png" /><figcaption>Foto de <a href="https://unsplash.com/@shemul?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Shahadat Shemul</a> <a href="https://unsplash.com/search/photos/command-line?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">emUnsplash</a></figcaption></figure><p>A <a href="https://twitter.com/flutterportugal">Flutter Portugal</a> tem o orgulho de anunciar um novo projecto: tradução para português de alguns dos mais visto artigos sobre Flutter ou Dart! Queremos que a informação seja acessível a todos os que queiram criar aplicações ou websites com a nova plataforma da Google, independentemente do seu grau de conhecimento de Inglês.</p><p>Fiquem atentos às próximas semanas pois teremos mais artigos a caminho!</p><p>Artigo original de <a href="https://www.ign.com/articles/2019/08/26/decay-of-logos-review">Gonçalo Palma,</a> <a href="https://medium.com/flutter-community/flutter-and-the-command-line-a-love-story-a3648ef2411">Flutter and the Command Line — a Love Story</a></p><p>Usar um IDE (Ambiente de Desenvolvimento Integrado), como o <a href="https://developer.android.com/studio?authuser=1">Android Studio</a> da IntelliJ ou o<a href="https://code.visualstudio.com/"> Visual Studio Code</a>, para desenvolver as aplicações em Flutter, torna as nossas vidas mais fáceis. Com o clique de um botão podemos fazer correr a nossa aplicação, <em>debug</em> (depurá-la), fazer <em>hot-reload</em>, etc… E embora possamos ter teclas de atalho para dispensar o uso do rato, pode ser incómodo ter de mover o rato pelo ecrã. Ou, simplesmente gostamos de usar o<br>terminal, ou estamos curiosos sobre ele e queremos saber diferentes formas de interagir com o Flutter.</p><p>Felizmente, o Flutter vem com um CLI (Interface de Linha de Comando, ILC) que nos permite facilmente fazer as mesmas tarefas que seriam feitas em qualquer IDE, como $ flutter doctor, o qual mostra se tudo correu bem com a nossa Instalação Flutter e verifica as últimas atualizações do repositório Flutter no Github.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LHAbsCWMQPUQJgMM70KRBA.png" /><figcaption>flutter doctor</figcaption></figure><p>Comecemos então a explorar o CLI para ver o que é que podemos usar no dia-a-dia.</p><h3>Explorando a secção de “Ajuda”</h3><p>Usar o $ flutter help vai mostrar-nos uma longa e intimidante lista de comandos, tais como analyze, build, channel, clean, create, install, logs, pub e run, cada qual com uma pequena descrição. Mas como podemos usar cada um dos comandos?</p><p>Tal como com muitos outros CLI, podemos utilizar o útil argumento -help depois de qualquer comando para que o CLI nos mostre um pequeno ecrã de ajuda.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dimkyqhrL0gdOOx23vB0tA.png" /><figcaption>Parte dos logs de flutter — help</figcaption></figure><p>Neste caso concreto, se utilizarmos o seguinte comando:</p><pre>$ flutter analyze — watch — no-pub</pre><p>Iremos analisar o código do projeto atual do Flutter enquanto estamos a analizar continuamente qualquer mudança em todos os ficheiros (continuously, watching the filesystem for changes, -watch ) e sem obter ou atualizar as nossas dependências do pub --no-pub.</p><h3>Correr os nossos Projetos</h3><p>Quando estamos a desenvolver uma aplicação seja de que tipo for, aquilo de que um programador necessita é de correr o seu código. Afinal, como é que podemos admirar aquilo que produzimos (ou desesperar de cada vez que um novo bug tenta arruinar o nosso progresso)?</p><p>Para tal, tudo o que temos de fazer é usar o simples comando:</p><pre>$ flutter run</pre><p>O qual é simples o suficiente de utilizar, certo? Mas, às vezes, podemos ter mais do que um dispositivo (ou simulador) ligado à nossa máquina e, assim, aparece a mensagem de erro seguinte:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_UnX3QQV7bh5KyJXW5odoA.png" /><figcaption>Comando flutter run quando se tem mais do que um device conectado</figcaption></figure><p>Por isso, façamos o que nos é dito e usemos a flag correta para o nosso dispositivo ao utilizar -d &lt;device id&gt;. Assumindo que o ID do nosso dispositivo é emulator-4 , podemos executar o nosso projeto para esse dispositivo ao usar:</p><pre>$ flutter run -d emulator-4</pre><p>Agora é que começa a ficar interessante. Digamos que os utilizadores da nossa aplicação, <a href="https://flutter.dev/docs/deployment/flavors">Flavors</a>, e nós, temos pontos de entrada diferentes para a nossa aplicação: main.dart e main_dev.dart para <em>flavors/schemes</em> prod e dev.Como é que podemos executar cada um deles? Usemos o comando -help e procuremos argumentos que nos possam ajudar.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rcFn-ZZ7FKbuKLvfwfCkHg.png" /><figcaption>Flutter run help</figcaption></figure><p>Tal como visto acima, podemos utilizar as <em>flags</em> --flavors e -t para nos poderem auxiliar na escolha de um Flavor e de um <em>entry point</em> (ponto de entrada), respetivamente. Desta forma, podemos ter dois comandos diferentes para executar a nossa aplicação:</p><pre>$ flutter run -d emulator-4 -t lib/main_dev.dart — flavor dev<br>$ flutter run -d emulator-4 -t lib/main.dart — flavor prod</pre><p>Isto vai pôr a nossa aplicação a funcionar! 🚀</p><p>Mas… Está a faltar algo.</p><p>O Flutter é conhecido pela característica de <a href="https://flutter.dev/docs/development/tools/hot-reload"><em>Hot Reload</em></a>, à qual podemos aceder com facilidade no nosso IDE preferido, a maior parte das vezes ao clicar num símbolo ⚡. Mas como é que podemos fazer isso com a linha de comandos? A única referência ao hot reload que temos está no argumento --hot, mas este só nos permite ativar ou desativar essa função.</p><p>Se prestarmos especial atenção ao output da consola quando executamos a nossa aplicação, vemos que faz menção aos elementos de Hot Reload e Hot Restart. Só que, em vez de usar argumentos designados (named arguments), a linha de comando incita-nos a escrever cada carater de que precisemos.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*J1l6S4RAzRMrqeZL9wYTQw.png" /><figcaption>Flutter run</figcaption></figure><p>Então, vamos seguir as suas instruções e clicar em r.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/684/1*JMa5n_-CdZj2PzKuFoteQw.png" /><figcaption>Hot reload</figcaption></figure><p>E como podemos fazer Hot Restart? Usando Shift + R</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/598/1*ePAlNEDbIEUI_F2tDct3yg.png" /><figcaption>Hot restart</figcaption></figure><p>E é isto! Mas, que outras funcionalidades estão escondidas por detrás do comando run? Podemos vê-las ao usar a tecla h.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*skn0sG1TEO2q7whfQad-IA.png" /><figcaption>Detailed help for Flutter run</figcaption></figure><p>Então, por exemplo, se clicarmos em p, teremos uma útil sobreposição da UI (Interface do Utilizador, IU) com as diretrizes de construção para cada widget, o que se pode revelar vantajoso quando se tentam solucionar bugs desagradáveis da UI.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/828/1*YvZviJL4YVgITrA3jQjCKA.png" /><figcaption>Flutter app com as diretrizes de construção dos Widgets</figcaption></figure><p>(Dica: Esta captura de ecrã foi feita usando a tecla s, o que guarda a imagem no diretório do projeto)</p><h3>Os channels (canais) Flutter</h3><p>Outro comando útil do CLI Flutter é a habilidade de mudar o atual canal Flutter. Um canal não é mais do que o ramo git no qual estamos a obter o código da fonte Flutter. Quando é que precisamos de o mudar? Quando estamos a usar ferramentas experimentais, tais como o <a href="https://github.com/flutter/flutter_web">Flutter Web</a> ou se não podemos esperar que o bug-fix apareça no canal (<em>channel</em>) stable.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/362/1*iT9ejhxcaQ4hWG4OtL53hQ.png" /><figcaption>Flutter channels — stable channel currently selected</figcaption></figure><p>Depois de mudarmos o canal através do $ flutter channel master, deveremos sempre usar o comando $ flutter doctor para verificar se as ferramentas Flutter são as corretas ou se precisamos de atualizar a nossa versão do Flutter, o qual podemos fazer ao utilizar $ flutter upgrade.</p><h3>Logs de Flutter</h3><p>$ flutter logs -d emulator-4</p><p>Este fala por si: mostra os logs (registos) Flutter do dispositivo. Mas não só os logs de Flutter para a aplicação em execução, como também para todas as aplicações que usam o Flutter no dispositivo ligado.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*oSyJYC_d0-tAkNFg.png" /><figcaption>Flutter logs help</figcaption></figure><p>Porque é que isto é útil?</p><ul><li>Pode ajudar-nos a verificar se na versão --release da aplicação (ou até na versão enviada para a App Store/Play Store) existem <em>logs</em> que nos tenhamos esquecido de esconder (pode ler mais sobre isso <a href="https://medium.com/flutter-community/debugprint-and-the-power-of-hiding-and-customizing-your-logs-in-dart-86881df05929">neste artigo sobre debugPrint</a>).</li><li>Pode ajudar-nos a economizar tempo para ver os <em>logs</em> da aplicação atual, sem necessitar de voltar a correr a aplicação.</li><li>Se, por alguma razão, $ flutter run emite os <em>logs</em> do sistema juntamente com os <em>logs </em>de Flutter, pode abrir um novo separador terminal e executar $ flutter logs para ver apenas os registos Flutter para esse dispositivo.</li></ul><h3>Criar um Novo Projeto</h3><p>Correr um novo projeto e mudar o canal de Flutter pode ser bastante divertido, mas tudo isso pode resultar em nada se não tivermos um projeto onde os testar!</p><p>Aquando da criação de um projeto, poderemos estar interessados em:</p><ul><li>Dar-lhe um nome;</li><li>Nomear uma organização para o nome do pacote (<em>p</em>ackage<em>)</em> (por exemplo, com.vanethos)</li><li>A linguagem usada para o projeto Android (Kotlin ou Java).</li><li>A linguagem usada para o projeto iOS (Swift ou Objective-C)</li></ul><p>Felizmente, podemos encontrar uma solução para cada um deles nos argumentos que se seguem:</p><figure><img alt="" src="https://cdn-images-1.medium.com/proxy/0*O83c1uU54g6IO1SL.png" /><figcaption>Flutter create help</figcaption></figure><p>Assim, para criar um novo projeto na pasta flutter_cli_test com o prefixo do pacote com.vanethos, nome do projeto clitest e usando Kotlin e Swift, sem o AndroidX, precisamos de utilizar:</p><pre>$ flutter create — org com.vanethos — project-name flutter_cli_test -a kotlin -i swift flutter_cli_test</pre><p>E, se não soubéssemos como executar o nosso projeto, o Flutter guia-nos para que o executemos diretamente a partir da linha de comando, o que é bastante bom!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aXJi3hT471zo5lkNl6MHDA.png" /><figcaption>Flutter create após criar um projecto</figcaption></figure><h3>Criar Scripts Personalizados em Bash</h3><p>Nós sabemos como criar e executar projetos e, se olharmos para a secção de Ajuda, também sabemos como limpar o nosso projeto (usando $ flutter clean).</p><p>No entanto, os nossos princípios dizem-nos que, para cada execução do projeto, devemos:</p><ul><li>Limpar o projeto Android</li><li>Limpar o projeto Flutter</li><li>Executar o Flavor dev da nossa aplicação no nosso dispositivo preferido</li></ul><p>O que quer dizer que teremos de escrever o seguinte no terminal:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d2dcf0506dc0d982a1cdc5e916b7faa7/href">https://medium.com/media/d2dcf0506dc0d982a1cdc5e916b7faa7/href</a></iframe><p>E se conseguíssemos simplesmente escrever um único comando para que a aplicação fosse executada em cada flavour?</p><p>No MacOs, poderemos ter de procurar como é que se criam scripts bash. Felizmente, Tania Rascia escreveu um <a href="https://www.taniarascia.com/how-to-create-and-use-bash-scripts/">ótimo artigo</a>, no qual podemos ver que, para poder criar um script bash, precisamos de criar um ficheiro onde, na primeira linha, adicionamos o seguinte:<br>Assim, agora já podemos criar um simples guião bash, ao:</p><ul><li>Criar um ficheiro chamado dev_build.sh (podemos fazê-lo com o comando touch dev_build.sh )</li><li>Adicionar o seguinte ao ficheiro:</li></ul><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/64501aed45c185d5edff9b99abb2c862/href">https://medium.com/media/64501aed45c185d5edff9b99abb2c862/href</a></iframe><p>Como é que o podemos fazer correr? Simplesmente chamando o ficheiro, usando:<br>$ sh dev_build.sh</p><p>E voilà! Agora podemos, com um só comando, limpar o nosso código minuciosamente e correr a nossa aplicação num Flavor específico!</p><h3>Dica bónus: Usar o Pubx</h3><p>Vamos levar o nosso amor pela linha de comando mais longe: queremos procurar pacotes e adicioná-los ao nosso ficheiro pubspec.yaml?</p><p>Embora o Flutter não permita isto de origem, podemos confiar na comunidade Flutter para vir ao encontro dos nossos desejos e assim o fez, com o pacote <a href="https://pub.dev/packages/pubx">pubx</a>, de <a href="https://github.com/shyndman">Scott Hyndman</a>.</p><p>Quer procurar um pacote específico? Use $ pubx search provider</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/566/1*QLYzN7gapJvaQ6JprtGngQ.png" /><figcaption>pubx search</figcaption></figure><p>Encontrou um pacote e quer saber mais sobre ele? Use $ pubx view provider</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*VF-DQmTNCmfQvz9U.png" /><figcaption>pubx view</figcaption></figure><p>E, finalmente, quer adicionar dependências ao seu ficheiro pubspec.yaml a partir da linha de comando? Facilmente feito com o $ pubx add provider</p><h3>Conclusão</h3><p>Passámos por uma breve introdução ao CLI Flutter 💻 .</p><p>Cabe-nos escolher se esta é a opção certa para nós, ou não. Talvez gostemos de usar o terminal, talvez apenas queiramos usar o ADI e deixá-lo lidar com ele, ninguém nos deve julgar sobre isso. Porém, é sempre bom conhecer alternativas. Porquê?</p><ul><li>Com a linha de comando podemos executar um projeto sem o abrir</li><li>Podemos facilmente mudar o Canal Flutter</li><li>Podemos analisar o nosso código antes de um grande commit</li><li>Se quisermos testar um novo plug-in/pacote rapidamente, podemos fazê-lo através de:</li></ul><pre>$ git clone <a href="https://github.com/Vanethos/stream_disposable.git">https://github.com/Vanethos/stream_disposable.git</a><br>$ cd stream_disposable/examples<br>$ flutter run</pre><ul><li>Pode facilmente criar builds de release para a sua aplicação, através de:</li></ul><pre>$ flutter build ios<br>$ flutter build appbundle</pre><ul><li>Simplesmente parece fixe.</li></ul><p>Digam-nos a vossa opinião! 😄Vão usar? Quais são os vossos comandos preferidos?</p><ul><li><a href="https://medium.com/flutter-community/flutter-and-the-command-line-a-love-story-a3648ef2411">Flutter and the Command Line — a Love Story</a></li><li><a href="https://medium.com/@solid.goncalo">Gonçalo Palma - Medium</a></li><li><a href="https://twitter.com/FlutterPortugal">Flutter Portugal</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3f18e6c97136" width="1" height="1" alt=""><hr><p><a href="https://medium.com/flutter-portugal/o-flutter-e-a-linha-de-comandos-uma-hist%C3%B3ria-de-amor-3f18e6c97136">O Flutter e a Linha de Comandos — uma história de amor</a> was originally published in <a href="https://medium.com/flutter-portugal">Flutter Portugal</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>