Animating Apps With Flutter

Shubham

Acerca del autor

Desarrollador de software full stack de día y desarrollador de aplicaciones móviles de noche. Mientras no trabajo, me encanta cocinar y jugar a juegos de PC.More aboutShubham↬

  • 14 min read
  • Apps,Animation
  • Saved for offline reading
  • Share on Twitter, LinkedIn

Flutter proporciona un gran soporte de animación para aplicaciones multiplataforma. Este artículo explora la nueva forma no convencional y más fácil de añadir animaciones a las apps. ¿Qué son exactamente estos nuevos widgets de «Animación y Movimiento» y cómo podemos utilizarlos para añadir animaciones simples y complejas?

Las aplicaciones para cualquier plataforma son elogiadas cuando son intuitivas, bonitas y proporcionan una respuesta agradable a las interacciones del usuario. La animación es una de las formas de conseguirlo.

Flutter, un framework multiplataforma, ha madurado en los últimos dos años para incluir soporte web y de escritorio. Se ha ganado la reputación de que las aplicaciones desarrolladas con él son fluidas y atractivas. Con su rico soporte de animación, la forma declarativa de escribir la UI, «Hot Reload», y otras características, es ahora un completo framework multiplataforma.

Si estás empezando con Flutter y quieres aprender una forma poco convencional de añadir animación, entonces estás en el lugar correcto: exploraremos el reino de los widgets de animación y movimiento, una forma implícita de añadir animaciones.

Flutter se basa en el concepto de widgets. Cada componente visual de una aplicación es un widget – piensa en ellos como vistas en Android. Flutter proporciona soporte de animación utilizando una clase Animation, un objeto «AnimationController» para la gestión, y «Tween» para interpolar el rango de datos. Estos tres componentes trabajan juntos para proporcionar una animación suave. Dado que esto requiere la creación y gestión manual de la animación, se conoce como una forma explícita de animar.

Ahora déjame presentarte los widgets de animación y movimiento. Flutter proporciona numerosos widgets que soportan inherentemente la animación. No hay necesidad de crear un objeto de animación o cualquier controlador, ya que toda la animación es manejada por esta categoría de widgets. Sólo tienes que elegir el widget apropiado para la animación requerida y pasar los valores de las propiedades del widget para animar. Esta técnica es una forma implícita de animar.

Jerarquía de animación en Flutter. (Vista previa grande)

El gráfico anterior establece a grandes rasgos la jerarquía de animación en Flutter, cómo se admite tanto la animación explícita como la implícita.

Algunos de los widgets animados cubiertos en este artículo son:

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

Flutter no sólo proporciona widgets animados predefinidos, sino también un widget genérico llamado AnimatedWidget, que puede utilizarse para crear widgets animados implícitos personalizados. Como es evidente por el nombre, estos widgets pertenecen a la categoría de widgets animados y de movimiento, por lo que tienen algunas propiedades comunes que nos permiten hacer animaciones mucho más suaves y de mejor aspecto.

Déjame explicar ahora estas propiedades comunes, ya que se utilizarán más adelante en todos los ejemplos.

  • duration
    La duración sobre la que animar los parámetros.
  • reverseDuration
    La duración de la animación inversa.
  • curve
    La curva a aplicar al animar los parámetros. Los valores interpolados pueden tomarse de una distribución lineal o, si se especifica, pueden tomarse de una curva.

Comencemos el viaje creando una sencilla aplicación que llamaremos «Quoted». Mostrará una cita aleatoria cada vez que se inicie la aplicación. Dos cosas a tener en cuenta: en primer lugar, todas estas citas serán hardcoded en la aplicación; y en segundo lugar, no se guardarán los datos del usuario.

Nota: Todos los archivos de estos ejemplos se pueden encontrar en GitHub.

Cómo empezar

Flutter debe estar instalado y necesitarás cierta familiaridad con el flujo básico antes de seguir adelante. Un buen lugar para empezar es, «Using Google’s Flutter For Truly Cross-Platform Mobile Development».

Crea un nuevo proyecto Flutter en Android Studio.

Menú del nuevo proyecto Flutter en Android Studio. (Vista previa grande)

Esto abrirá un nuevo asistente de proyecto, donde puedes configurar los fundamentos del proyecto.

Pantalla de selección del tipo de proyecto Flutter. (Vista previa grande)

En la pantalla de selección del tipo de proyecto, hay varios tipos de proyectos Flutter, cada uno de los cuales atiende a un escenario específico.. Para este tutorial, elija Aplicación Flutter y pulse Siguiente.

Ahora necesita introducir alguna información específica del proyecto: el nombre y la ruta del proyecto, el dominio de la empresa, etc. Echa un vistazo a la siguiente imagen.

Pantalla de configuración de la aplicación Flutter. (Vista previa grande)

Agrega el nombre del proyecto, la ruta del SDK de Flutter, la ubicación del proyecto y una descripción opcional del proyecto. Pulse Siguiente.

Pantalla del nombre del paquete de la aplicación Flutter. (Vista previa grande)

Cada aplicación (ya sea Android o iOS) requiere un nombre de paquete único. Normalmente, se utiliza el inverso del dominio de su sitio web; por ejemplo, com.google o com.yahoo. Pulse Finalizar para generar una aplicación Flutter que funcione.

El proyecto de ejemplo generado. (Vista previa grande)

Una vez generado el proyecto, deberías ver la pantalla que se muestra arriba. Abra el archivo main.dart (resaltado en la captura de pantalla). Este es el archivo principal de la aplicación. El proyecto de ejemplo está completo en sí mismo, y puede ejecutarse directamente en un emulador o en un dispositivo físico sin ninguna modificación.

Sustituye el contenido del archivo main.dart por el siguiente fragmento de código:

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 limpia el archivo main.dart añadiendo simplemente información relevante para crear una nueva app. La clase MyApp devuelve un objeto: un widget MaterialApp, que proporciona la estructura básica para crear apps conformes a Material Design. Para hacer el código más estructurado, crea dos nuevos archivos dart dentro de la carpeta lib: FirstPage.dart y Quotes.dart.

El archivo FirstPage.dart. (Vista previa grande)

FirstPage.dart contendrá todo el código responsable de todos los elementos visuales (widgets) necesarios para nuestra aplicación Quoted. Toda la animación se maneja en este archivo.

Nota: Más adelante en el artículo, todos los fragmentos de código para cada widget animado se añaden a este archivo como hijos del widget Scaffold. Para más información, este ejemplo en GitHub podría ser útil.

Comienza añadiendo el siguiente código a FirstPage.dart. Este es el código parcial donde se añadirán otras cosas más 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: , ), ); }}
El archivo Quotes.dart. (Vista previa grande)

El archivo Quotes.dart contiene una lista de todas las citas codificadas. Un punto a tener en cuenta aquí es que la lista es un objeto estático. Esto significa que se puede utilizar en otros lugares sin crear un nuevo objeto de la clase Quotes. Esto se ha elegido por diseño, ya que la lista anterior actúa simplemente como una utilidad.

Añade el siguiente código a este archivo:

class Quotes { static const quotesArray = ;}

El esqueleto del proyecto ya está listo, así que vamos a dar cuerpo a Quoted un poco más.

AnimatedOpacity

Para dar un toque personal a la aplicación, estaría bien saber el nombre del usuario, así que vamos a pedirlo y mostrar un botón de siguiente. Hasta que el usuario introduzca su nombre, este botón estará oculto, y se mostrará con gracia cuando se dé un nombre. Necesitamos algún tipo de animación de visibilidad para el botón, pero ¿hay un widget para eso? Sí, lo hay.

Entrar AnimatedOpacity. Este widget se basa en el widget de Opacidad añadiendo soporte de animación implícita. ¿Cómo lo utilizamos? Recuerda nuestro escenario: necesitamos mostrar un botón siguiente con visibilidad animada. Envolvemos el widget de botón dentro del widget AnimatedOpacity, introducimos algunos valores adecuados y añadimos una condición para desencadenar la animación – y Flutter puede encargarse del resto.

_getAnimatedOpacityButton() { return AnimatedOpacity( duration: Duration(seconds: 1), reverseDuration: Duration(seconds: 1), curve: Curves.easeInOut, opacity: showNextButton ? 1 : 0, child: _getButton(), );}
Animación de opacidad del botón siguiente. (Vista previa grande)

El widget AnimatedOpacitytiene dos propiedades obligatorias:

  • opacity
    Un valor de 1 significa completamente visible; 0 (cero) significa oculto. Mientras se anima, Flutter interpola valores entre estos dos extremos. Puedes ver cómo se coloca una condición para cambiar la visibilidad, desencadenando así la animación.
  • child
    El widget hijo que tendrá su visibilidad animada.

Ahora deberías entender lo realmente sencillo que es añadir animación de visibilidad con el widget implícito. Además, todos estos widgets siguen las mismas pautas y son fáciles de usar. Pasemos al siguiente.

AnimatedCrossFade

Tenemos el nombre del usuario, pero el widget sigue esperando la entrada. En el paso anterior, cuando el usuario introduce su nombre, mostramos el botón siguiente. Ahora, cuando el usuario pulsa el botón, quiero dejar de aceptar entradas y mostrar el nombre introducido. Hay muchas maneras de hacerlo, por supuesto, pero tal vez podamos ocultar el widget de entrada y mostrar un widget de texto no editable. Vamos a probarlo usando el widget AnimatedCrossFade.

Este widget requiere dos hijos, ya que el widget se desplaza entre ellos basándose en alguna condición. Una cosa importante a tener en cuenta mientras se utiliza este widget es que ambos hijos deben tener el mismo ancho. Si la altura es diferente, el widget más alto será recortado desde la parte inferior. En este escenario, se utilizarán dos widgets como hijos: input y 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, );}
Desplazamiento cruzado entre el widget input y el widget name. (Vista previa grande)

Este widget requiere un conjunto diferente de parámetros obligatorios:

  • crossFadeState
    Este estado resuelve qué hijo mostrar.
  • firstChild
    Especifica el primer hijo para este widget.
  • secondChild
    Especifica el segundo hijo.

AnimatedAlign

En este punto, la etiqueta del nombre se posiciona en el centro de la pantalla. Se verá mucho mejor en la parte superior, ya que necesitamos el centro de la pantalla para mostrar las comillas. En pocas palabras, la alineación del widget de la etiqueta del nombre debe cambiarse del centro a la parte superior. Y ¿no sería bueno animar este cambio de alineación junto con la animación de fundido cruzado anterior? Hagámoslo.

Como siempre, se pueden utilizar varias técnicas para conseguirlo. Dado que el widget de la etiqueta de nombre ya está alineado al centro, animar su alineación sería mucho más sencillo que manipular los valores superior e izquierdo del widget. El widget AnimatedAlign es perfecto para este trabajo.

Para iniciar esta animación, se requiere un disparador. El único propósito de este widget es animar el cambio de alineación, por lo que sólo tiene unas pocas propiedades: añadir un hijo, establecer su alineación, disparar el cambio de alineación, y eso es todo.

_getAnimatedAlignWidget() { return AnimatedAlign(duration: Duration(seconds: 1),curve: Curves.easeInOut,alignment: alignTop ? Alignment.topLeft : Alignment.center,child: _getAnimatedCrossfade(), );}
Animación de la alineación del widget del nombre. (Vista previa grande)

Sólo tiene dos propiedades obligatorias:

  • child:
    El child cuya alineación será modificada.
  • alignment:
    Valor de alineación requerido.

Este widget es realmente sencillo pero los resultados son elegantes. Además, hemos visto lo fácil que es utilizar dos widgets animados diferentes para crear una animación más compleja. Esta es la belleza de los widgets animados.

AnimatedPadding

Ahora tenemos el nombre del usuario en la parte superior, animado suavemente sin mucho esfuerzo, utilizando diferentes tipos de widgets animados. Vamos a añadir un saludo, «Hola», antes del nombre. Añadiendo un widget de texto con valor «Hola,» en la parte superior hará que se superponga al widget de texto de saludo, quedando como la imagen de abajo.

Los widgets de saludo y nombre se superponen. (Vista previa grande)

¿Y si el widget de texto del nombre tuviera algo de relleno a la izquierda? Aumentar el relleno a la izquierda definitivamente funcionará, pero espera: ¿podemos aumentar el relleno con alguna animación? Sí, y eso es lo que hace AnimatedPadding. Para que todo esto se vea mucho mejor, hagamos que el widget de texto de saludo se desvanezca y que el relleno del widget de texto de nombre aumente al mismo tiempo.

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

Dado que la animación anterior debe ocurrir sólo después de que la alineación animada anterior se haya completado, necesitamos retrasar la activación de esta animación. Desviándonos brevemente del tema, este es un buen momento para hablar de un mecanismo popular para añadir retraso. Flutter proporciona varias técnicas de este tipo, pero el constructor Future.delayed es uno de los enfoques más simples, limpios y legibles. Por ejemplo, para ejecutar un trozo de código después de 1 segundo:

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

Como ya se conoce la duración del retardo (calculada a partir de las duraciones de las animaciones anteriores), la animación puede activarse después de este 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; }); });}
Animación del widget del nombre. (Vista previa grande)

Este widget sólo tiene dos propiedades obligatorias:

  • child
    El hijo dentro de este widget, al que se aplicará el relleno.
  • padding
    La cantidad de espacio a añadir.

AnimatedSize

Hoy en día, cualquier aplicación que tenga algún tipo de animación incluirá el acercamiento o alejamiento de los componentes visuales para captar la atención del usuario (comúnmente llamado animación de escalado). Por qué no utilizar la misma técnica aquí? Podemos mostrar al usuario una cita motivadora que se acerque desde el centro de la pantalla. Permítame presentarle el widget AnimatedSize, que permite los efectos de acercamiento y alejamiento, controlados por el cambio de tamaño de su hijo.

Este widget es un poco diferente de los demás en lo que respecta a los parámetros necesarios. Necesitamos lo que Flutter llama un «Ticker». Flutter tiene un método para que los objetos sepan cada vez que se dispara un nuevo evento de marco. Puede ser pensado como algo que envía una señal diciendo: «¡Hazlo ahora! … ¡Hazlo ahora! … ¡Hazlo ahora! …»

El widget AnimatedSize requiere una propiedad – vsync – que acepta un proveedor de ticker. La forma más fácil de obtener un proveedor de ticker es añadir un Mixin a la clase. Hay dos implementaciones básicas de proveedores de ticker: SingleTickerProviderStateMixin, que proporciona un único ticker; y TickerProviderStateMixin, que proporciona varios.

La implementación por defecto de un Ticker se utiliza para marcar los frames de una animación. En este caso, se emplea esta última. Más 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; }); });}
Animación del widget de cotizaciones. (Vista previa grande)

Propiedades obligatorias para este widget:

  • vsync
    El proveedor de ticker requerido para coordinar la animación y los cambios de frame.<
  • child
    El hijo cuyos cambios de tamaño serán animados.

La animación de acercamiento y alejamiento es ahora fácilmente domable.

AnimatedPositioned

¡Genial! Las comillas se acercan desde el centro para captar la atención del usuario. Qué pasaría si se deslizara desde la parte inferior mientras se hace el zoom? Vamos a probarlo. Este movimiento implica jugar con la posición del widget de la cita y animar los cambios en las propiedades de posición. AnimatedPositioned es el candidato perfecto.

Este widget transiciona automáticamente la posición del hijo durante una duración determinada cada vez que cambia la posición especificada. Un punto a tener en cuenta: sólo funciona si su widget padre es un «Stack». Este widget es bastante simple y sencillo de utilizar. Veamos.

// 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, );}
Posición con animación de escala de citas. (Vista previa grande)

Este widget sólo tiene una propiedad obligatoria:

  • child
    El widget cuya posición será cambiada.

Si no se espera que el tamaño del hijo cambie junto con su posición, una alternativa más performativa a este widget sería SlideTransition.

Aquí está nuestra animación completa:

Todos los widgets animados juntos. (Vista previa grande)

Conclusión

Las animaciones son una parte integral de la experiencia del usuario. Las aplicaciones estáticas o con animaciones poco atractivas no sólo reducen la retención de los usuarios, sino también la reputación de los desarrolladores a la hora de ofrecer resultados.

Hoy en día, la mayoría de las aplicaciones populares tienen algún tipo de animación sutil para deleitar a los usuarios. La respuesta animada a las peticiones de los usuarios también puede atraerlos a explorar más. Flutter ofrece un montón de características para el desarrollo multiplataforma, incluyendo un rico soporte para animaciones suaves y responsivas.

Flutter tiene un gran soporte de plugins que nos permite utilizar animaciones de otros desarrolladores. Ahora que ha madurado a la versión 1.9, con tanto amor de la comunidad, Flutter está obligado a mejorar en el futuro. Yo diría que ahora es un gran momento para aprender Flutter!

Leave a Reply