Animating Apps With Flutter

Shubham

About The Author

Uma pilha completa de desenvolvimento de software durante o dia e de aplicações móveis durante a noite. Embora não esteja trabalhando, eu adoro cozinhar e jogar jogos para PC.Mais aboutShubham↬

  • 14 min read
  • Apps,Animação
  • Salvado para leitura offline
  • Partilhar no Twitter, LinkedIn
Flutter fornece um grande suporte de animação para aplicações multi-plataforma. Este artigo explora o novo não convencional e uma forma mais fácil de adicionar animações a aplicações. O que são exatamente esses novos widgets “Animação e Movimento” e como podemos usá-los para adicionar animações simples e complexas?

Aplicações para qualquer plataforma são elogiadas quando são intuitivas, de boa aparência e fornecem um feedback agradável para as interações do usuário. Animação é uma das formas de fazer exatamente isso.

Flutter, uma estrutura multiplataforma, amadureceu nos últimos dois anos para incluir suporte web e desktop. Ganhou uma reputação de que os aplicativos desenvolvidos com ele são suaves e bonitos. Com seu rico suporte a animação, forma declarativa de escrever UI, “Hot Reload,” e outros recursos, é agora um framework completo para várias plataformas.

Se você está começando com Flutter e quer aprender uma forma não convencional de adicionar animação, então você está no lugar certo: nós vamos explorar o reino da animação e widgets de movimento, uma forma implícita de adicionar animações.

Flutter é baseado no conceito de widgets. Cada componente visual de um aplicativo é um widget – pense neles como vistas no Android. Flutter fornece suporte a animação usando uma classe de Animação, um objeto “AnimationController” para gerenciamento, e “Tween” para interpolar a gama de dados. Estes três componentes trabalham em conjunto para fornecer uma animação suave. Uma vez que isto requer a criação e gestão manual de animação, é conhecida como uma forma explícita de animação.

Agora deixe-me apresentar-lhe os widgets de animação e movimento. Flutter fornece inúmeros widgets que suportam inerentemente animação. Não há necessidade de criar um objecto de animação ou qualquer controlador, pois toda a animação é tratada por esta categoria de widgets. Basta escolher o widget apropriado para a animação desejada e passar nos valores das propriedades do widget para animar. Esta técnica é uma forma implícita de animar.

Hierarquia de animação em Flutter. (Preview grande)

O gráfico acima apresenta de forma aproximada a hierarquia da animação em Flutter, como ambas as animações explícitas e implícitas são suportadas.

Alguns dos widgets animados abordados neste artigo são:

  • AnimatedOpacity
  • AnimatedCrossFade
  • AnimatedAlign
  • AnimatedPadding
  • AnimatedSize
  • AnimatedPositioned.

Flutter não só fornece widgets animados predefinidos, mas também um widget genérico chamado AnimatedWidget, que pode ser usado para criar widgets animados personalizados implicitamente. Como o nome indica, estes widgets pertencem à categoria de widgets animados e de movimento, e por isso têm algumas propriedades comuns que nos permitem tornar as animações muito mais suaves e com melhor aspecto.

Deixe-me explicar estas propriedades comuns agora, pois serão usadas mais tarde em todos os exemplos.

  • duration
    A duração sobre a qual animar os parâmetros.
  • reverseDuration
    A duração da animação inversa.
  • curve
    A curva a aplicar ao animar os parâmetros. Os valores interpolados podem ser tomados de uma distribuição linear ou, se e quando especificados, podem ser tomados de uma curva.

Vamos começar a jornada criando um aplicativo simples que chamaremos de “Citado”. Ele exibirá uma citação aleatória toda vez que a aplicação for iniciada. Duas coisas a notar: primeiro, todas essas citações serão codificadas no aplicativo; e segundo, nenhum dado do usuário será salvo.

Note: Todos os arquivos para esses exemplos podem ser encontrados no GitHub.

Get Started

Flutter deve ser instalado e você precisará de alguma familiaridade com o fluxo básico antes de seguir em frente. Um bom lugar para começar é, “Using Google’s Flutter For Truly Cross-Platform Mobile Development”.

Criar um novo projeto Flutter no Android Studio.

Novo menu de projeto Flutter no Android Studio. (Pré-visualização grande)

Agenda um novo assistente de projeto, onde você pode configurar o básico do projeto.

Tela de seleção do tipo de projeto Flutter. (Visualização grande)

Na tela de seleção do tipo de projeto, existem vários tipos de projetos Flutter, cada um atendendo a um cenário específico… Para este tutorial, escolha Flutter Application e pressione Next.

Precisa agora de introduzir alguma informação específica do projecto: o nome do projecto e o caminho, o domínio da empresa, etc. Dê uma olhada na imagem abaixo.

Tela de configuração da aplicação Flutter. (Visualização grande)

Adicionar o nome do projeto, o caminho do Flutter SDK, a localização do projeto e uma descrição opcional do projeto. Pressione Next.

Tela de nome do pacote da aplicação Flutter. (Visualização grande)

Cada aplicação (seja Android ou iOS) requer um nome de pacote único. Normalmente, você usa o reverso do domínio do seu site; por exemplo, com.google ou com.yahoo. Pressione Finish para gerar um aplicativo Flutter funcional.

O projeto de amostra gerado. (Visualização grande)

Após o projeto ser gerado, você deve ver a tela mostrada acima. Abra o arquivo main.dart (destacado na captura de tela). Este é o arquivo principal da aplicação. O projeto de exemplo é completo em si mesmo, e pode ser executado diretamente em um emulador ou dispositivo físico sem qualquer modificação.

Substitua o conteúdo do arquivo main.dart pelo seguinte código snippet:

import 'package:animated_widgets/FirstPage.dart';import 'package:flutter/material.dart';void main() => runApp(MyApp());class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Animated Widgets', debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, accentColor: Colors.redAccent, ), home: FirstPage(), ); }}

Este código limpa o arquivo main.dart simplesmente adicionando informações simples relevantes para a criação de um novo aplicativo. A classe MyApp retorna um objeto: um widget MaterialApp, que fornece a estrutura básica para a criação de aplicativos em conformidade com o Material Design. Para tornar o código mais estruturado, crie dois novos arquivos de dardos dentro da pasta lib: FirstPage.dart e Quotes.dart.

O arquivo FirstPage.dart. (Preview grande)

FirstPage.dart conterá todo o código responsável por todos os elementos visuais (widgets) necessários para o nosso aplicativo Quoted. Toda a animação é tratada neste arquivo.

Note: Mais tarde no artigo, todos os trechos de código para cada widget animado são adicionados a este arquivo como filhos do widget Scaffold. Para mais informações, este exemplo no GitHub pode ser útil.

Inicie adicionando o seguinte código ao FirstPage.dart. Este é o código parcial onde outras coisas serão adicionadas mais tarde.

import 'dart:math';import 'package:animated_widgets/Quotes.dart';import 'package:flutter/material.dart';class FirstPage extends StatefulWidget { @override State createState() { return FirstPageState(); }}class FirstPageState extends State with TickerProviderStateMixin { bool showNextButton = false; bool showNameLabel = false; bool alignTop = false; bool increaseLeftPadding = false; bool showGreetings = false; bool showQuoteCard = false; String name = ''; double screenWidth; double screenHeight; String quote; @override void initState() { super.initState(); Random random = new Random(); int quoteIndex = random.nextInt(Quotes.quotesArray.length); quote = Quotes.quotesArray; } @override Widget build(BuildContext context) { screenWidth = MediaQuery.of(context).size.width; screenHeight = MediaQuery.of(context).size.height; return Scaffold( appBar: _getAppBar(), body: Stack( children: , ), ); }}

O arquivo Quotes.dart. (Preview grande)

O arquivo Quotes.dart contém uma lista de todas as citações hardcoded. Um ponto a notar aqui é que a lista é um objecto estático. Isto significa que ela pode ser usada em outros lugares sem criar um novo objeto da classe Quotes. Isto é escolhido por design, pois a lista acima age simplesmente como um utilitário.

Adicionar o seguinte código a este ficheiro:

class Quotes { static const quotesArray = ;}

O esqueleto do projecto está agora pronto, por isso vamos dar um pouco mais de profundidade Citações.

AnimatedOpacity

Para dar um toque pessoal à aplicação, seria bom saber o nome do utilizador, por isso vamos pedi-lo e mostrar um próximo botão. Até o usuário digitar seu nome, este botão fica oculto, e aparecerá graciosamente quando um nome for dado. Precisamos de algum tipo de animação de visibilidade para o botão, mas existe algum widget para isso? Sim, existe.

Enter AnimatedOpacity. Este widget é construído sobre o widget Opacity adicionando suporte implícito à animação. Como é que o usamos? Lembre-se do nosso cenário: precisamos de mostrar um próximo botão com visibilidade animada. Embrulhamos o botão widget dentro do widget AnimatedOpacity, alimentamos em alguns valores apropriados e adicionamos uma condição para acionar a animação – e Flutter pode lidar com o resto.

_getAnimatedOpacityButton() { return AnimatedOpacity( duration: Duration(seconds: 1), reverseDuration: Duration(seconds: 1), curve: Curves.easeInOut, opacity: showNextButton ? 1 : 0, child: _getButton(), );}
Opacity animation of next button. (Visualização grande)

O AnimatedOpacity widget tem duas propriedades obrigatórias:

  • opacity
    Um valor de 1 significa completamente visível; 0 (zero) significa oculto. Enquanto anima, Flutter interpola valores entre estes dois extremos. Você pode ver como uma condição é colocada para alterar a visibilidade, desencadeando assim a animação.
  • child
    O widget criança que terá a sua visibilidade animada.

Você deve agora entender como é realmente simples adicionar animação de visibilidade com o widget implícito. E todos esses widgets seguem as mesmas diretrizes e são fáceis de usar. Vamos passar para o próximo.

AnimatedCrossFade

Temos o nome do usuário, mas o widget ainda está esperando por entrada. No passo anterior, quando o usuário digita seu nome, nós mostramos o próximo botão. Agora, quando o usuário apertar o botão, quero parar de aceitar a entrada e mostrar o nome inserido. Há muitas maneiras de fazê-lo, é claro, mas talvez possamos esconder o widget de entrada e mostrar um widget de texto não editável. Vamos experimentar usando o widget AnimatedCrossFade widget.

Este widget requer dois filhos, já que o widget se cruza entre eles com base em alguma condição. Uma coisa importante a ter em mente ao usar este widget é que ambas as crianças devem ter a mesma largura. Se a altura é diferente, então o widget mais alto é cortado de baixo. Neste cenário, dois widgets serão usados como crianças: input e label.

_getAnimatedCrossfade() { return AnimatedCrossFade( duration: Duration(seconds: 1), alignment: Alignment.center, reverseDuration: Duration(seconds: 1), firstChild: _getNameInputWidget(), firstCurve: Curves.easeInOut, secondChild: _getNameLabelWidget(), secondCurve: Curves.easeInOut, crossFadeState: showNameLabel ? CrossFadeState.showSecond : CrossFadeState.showFirst, );}
Cross-fading entre o widget de input e o widget de nome. (Visualização grande)

Este widget requer um conjunto diferente de parâmetros obrigatórios:

  • crossFadeState
    Este estado determina qual criança mostrar.
  • firstChild
    Especifica a primeira criança para este widget.
  • secondChild
    Especifica a segunda criança.

Alinhamento Animizado

Neste ponto, a etiqueta do nome é posicionada no centro da tela. Ela ficará muito melhor no topo, pois precisamos do centro da tela para mostrar as citações. Simplificando, o alinhamento do widget da etiqueta do nome deve ser mudado do centro para o topo. E não seria bom animar esta mudança de alinhamento juntamente com a animação anterior de cross-fade? Vamos fazer isso.

Como sempre, várias técnicas podem ser usadas para conseguir isso. Como o nome widget de etiqueta já está alinhado ao centro, animar o seu alinhamento seria muito mais simples do que manipular os valores superior e esquerdo do widget. O widget AnimatedAlign é perfeito para este trabalho.

Para iniciar esta animação, um gatilho é necessário. O único propósito deste widget é animar a mudança de alinhamento, então ele tem apenas algumas propriedades: adicionar uma criança, definir seu alinhamento, acionar a mudança de alinhamento, e pronto.

_getAnimatedAlignWidget() { return AnimatedAlign(duration: Duration(seconds: 1),curve: Curves.easeInOut,alignment: alignTop ? Alignment.topLeft : Alignment.center,child: _getAnimatedCrossfade(), );}
Animação de alinhamento do nome widget. (Visualização grande)

Tem apenas duas propriedades obrigatórias:

  • criança:
    A criança cujo alinhamento será modificado.
  • alinhamento:
    Alinhamento requerido.

Este widget é realmente simples mas os resultados são elegantes. Além disso, vimos como podemos facilmente usar dois widgets animados diferentes para criar uma animação mais complexa. Esta é a beleza dos widgets animados.

AnimatedPadding

Agora temos o nome do usuário no topo, suavemente animado sem muito esforço, usando diferentes tipos de widgets animados. Vamos adicionar uma saudação, “Olá”, antes do nome. Adicionando um widget de texto com o valor “Olá”, no topo irá sobrepor o widget de texto de saudação, parecido com a imagem abaixo.

Os widgets de saudação e nome se sobrepõem. (Pré-visualização grande)

E se o widget de texto de saudação tivesse algum acolchoamento à esquerda? Aumentar o acolchoamento à esquerda vai definitivamente funcionar, mas espere: podemos aumentar o acolchoamento com alguma animação? Sim, e isso é o que AnimatedPadding faz. Para tornar tudo isso muito mais bonito, vamos fazer com que o widget de texto de saudação desapareça e o padding do nome widget de texto aumente ao mesmo tempo.

_getAnimatedPaddingWidget() { return AnimatedPadding( duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn, padding: increaseLeftPadding ? EdgeInsets.only(left: 28.0) : EdgeInsets.only(left: 0), child: _getAnimatedCrossfade(), );}

Desde que a animação acima só deve ocorrer após o alinhamento animado anterior estar completo, precisamos atrasar o acionamento dessa animação. Digerindo do tópico brevemente, este é um bom momento para falar sobre um mecanismo popular para adicionar atraso. Flutter fornece várias dessas técnicas, mas o construtor Future.delayed é uma das abordagens mais simples, mais limpa e mais legível. Por exemplo, para executar uma peça de código após 1 segundo:

Future.delayed(Duration(seconds: 1), (){ sum = a + b; // This sum will be calculated after 1 second. print(sum);});

Desde que a duração do atraso já seja conhecida (calculada a partir de durações de animação anteriores), a animação pode ser acionada após esse intervalo.

// Showing "Hi" after 1 second - greetings visibility trigger._showGreetings() { Future.delayed(Duration(seconds: 1), () { setState(() { showGreetings = true; }); });}// Increasing the padding for name label widget after 1 second - increase padding trigger._increaseLeftPadding() { Future.delayed(Duration(seconds: 1), () { setState(() { increaseLeftPadding = true; }); });}
Abrir animação do nome widget. (Visualização grande)

Este widget tem apenas duas propriedades obrigatórias:

  • child
    A criança dentro deste widget, ao qual o acolchoamento será aplicado.
  • padding
    A quantidade de espaço a adicionar.

AnimatedSize

Hoje em dia, qualquer aplicativo que tenha algum tipo de animação incluirá o zoom para dentro ou para fora dos componentes visuais para captar a atenção do usuário (comumente chamado de animação em escala). Por que não usar a mesma técnica aqui? Podemos mostrar ao usuário uma citação motivacional que faz zoom a partir do centro da tela. Deixe-me apresentar-lhe o widget AnimatedSize, que permite os efeitos de zoom-in e zoom-out, controlados alterando o tamanho do seu filho.

Este widget é um pouco diferente dos outros quando se trata dos parâmetros necessários. Precisamos do que o Flutter chama de “Ticker”. O Flutter tem um método para que os objetos saibam sempre que um novo evento de frame é acionado. Pode ser pensado como algo que envia um sinal dizendo: “Faça-o agora! … Faça-o agora! … Faça-o agora! …”

O widget AnimatedSize requer uma propriedade – vsync – que aceita um provedor de ticker. A maneira mais fácil de obter um provedor de ticker é adicionar um Mixin à classe. Existem duas implementações básicas de provedor de ticker: SingleTickerProviderStateMixin, que fornece um único ticker; e TickerProviderStateMixin, que fornece vários.

A implementação padrão de um ticker é usada para marcar os frames de uma animação. Neste caso, este último é utilizado. Mais sobre mixins.

// Helper method to create quotes card widget._getQuoteCardWidget() { return Card( color: Colors.green, elevation: 8.0, child: _getAnimatedSizeWidget(), );}// Helper method to create animated size widget and set its properties._getAnimatedSizeWidget() { return AnimatedSize( duration: Duration(seconds: 1), curve: Curves.easeInOut, vsync: this, child: _getQuoteContainer(), );}// Helper method to create the quotes container widget with different sizes._getQuoteContainer() { return Container( height: showQuoteCard ? 100 : 0, width: showQuoteCard ? screenWidth - 32 : 0, child: Center( child: Padding( padding: EdgeInsets.symmetric(horizontal: 16), child: Text(quote, style: TextStyle(color: Colors.white, fontWeight: FontWeight.w400, fontSize: 14),), ), ), );}// Trigger used to show the quote card widget._showQuote() { Future.delayed(Duration(seconds: 2), () { setState(() { showQuoteCard = true; }); });}
Escalar a animação do widget de citações. (Previsão grande)

Propriedades obrigatórias para este widget:

  • vsync
    O fornecedor do ticker necessário para coordenar animação e mudanças de quadros.<
  • child
    A criança cujo tamanho muda será animada.

A animação de aumentar e diminuir é agora facilmente domada.

AnimadaPosicionada

Grande! As aspas aumentam a partir do centro para chamar a atenção do usuário. E se deslizasse para cima a partir de baixo enquanto faz zoom in? Vamos tentar. Este movimento envolve brincar com a posição do widget de citações e animar as mudanças nas propriedades de posição. AnimatedPositioned é o candidato perfeito.

Este widget transita automaticamente a posição da criança ao longo de uma determinada duração sempre que a posição especificada muda. Um ponto a notar: ele só funciona se o widget pai for um “Stack”. Este widget é bastante simples e direto de usar. Vejamos.

// Helper method to create the animated positioned widget.// With position changes based on "showQuoteCard" flag._getAnimatedPositionWidget() { return AnimatedPositioned( duration: Duration(seconds: 1), curve: Curves.easeInOut, child: _getQuoteCardWidget(), top: showQuoteCard ? screenHeight/2 - 100 : screenHeight, left: !showQuoteCard ? screenWidth/2 : 12, );}
Posição com animação de escalas de aspas. (Visualização grande)

Este widget tem apenas uma propriedade obrigatória:

  • child
    O widget cuja posição será alterada.

Se não se espera que o tamanho da criança mude juntamente com a sua posição, uma alternativa mais performativa a este widget seria SlideTransition.

Aqui está a nossa animação completa:

Todos os widgets animados juntos. (Visualização grande)

Conclusão

Animações são parte integrante da experiência do usuário. Aplicações estáticas ou aplicações com animação janky não só menor retenção de usuários, mas também a reputação de um desenvolvedor para entregar resultados.

Hoje, a maioria das aplicações populares têm algum tipo de animação sutil para encantar os usuários. O feedback animado aos pedidos dos usuários também pode envolvê-los para explorar mais. Flutter oferece um monte de recursos para desenvolvimento multiplataforma, incluindo suporte rico para animações suaves e responsivas.

Flutter tem ótimo suporte a plug-in que nos permite usar animações de outros desenvolvedores. Agora que ele amadureceu para a versão 1.9, com tanto amor da comunidade, Flutter está destinado a melhorar no futuro. Eu diria que agora é um ótimo momento para aprender Flutter!

Leave a Reply