Creando fondo Parallax en Flutter

CarlosMillan
Comunidad Flutter
Published in
9 min readMay 16, 2019

Cómo construir un Tab Navigator con fondo Parallax en Flutter

Captura de pantalla del simulador de iOS que ejecuta el proyecto flutter-parallax-nav-demo

Hola amigos, este es una traducción al español de un artículo creado por Kenneth Reilly el cual muestra el uso de Parallax en Flutter. El artículo en inglés se encuentra aquí. Los aplausos son para el autor original de este post.

Introducción

En este artículo analizaremos el proceso de creación de un diseño de navegación con pestañas desplazable con fondo de Parallax sincronizado utilizando Dart y Flutter.

Dart con Flutter es una gran combinación para el desarrollo móvil. Al haber construido interfaces de usuario en todo, desde Silverlight y WPF a docenas de varias plataformas de escritorio, web y móviles, encuentro que Flutter proporciona un excelente entorno para crear rápidamente experiencias ricas en aplicaciones móviles.

Si aún no tiene el SDK de Flutter, diríjase a la página de instalación y siga las instrucciones de su sistema operativo. Si desea una copia del código fuente para el proyecto de demostración utilizado en este artículo, consulte aquí.

Empezando

Veamos la estructura del proyecto y su archivo de definición, pubspec.yaml:

Un archivo pubspec.yaml predeterminado con el paquete event_bus y los assets de imagen agregados

En el archivo de definición de proyecto pubspec.yaml, se ha agregado la dependencia event_bus. Un Bus de eventos permite a los listeners suscribirse a eventos y a los editores a activar estos eventos. Para obtener más información sobre el paquete event_bus para Dart y cómo funciona, consulte aquí. También en este archivo, la imagen de fondo se incluye como un archivo de recursos bajo assets. Esto permitirá que se haga referencia al archivo de imagen y se muestre desde la aplicación.

Contenedor de la Aplicación Principal

A continuación, veamos el archivo de entrada principal de la aplicación, lib/main.dart:

El archivo de entrada de la aplicación lib/main.dart con un widget builder de nivel superior

Este archivo define el componente de aplicación raíz ParallaxDemo, que es un StatelessWidget que devuelve un componente de MaterialApp con nuestro título de “Parallax BG”, algunos datos de tema predeterminados y el widget de AppContainer principal.

En este proyecto, las definiciones de las “pantallas” son solo las letras de la A a la E, que se muestran tanto en el icono de la pestaña como en el contenido de la pantalla para simplificar.

Tenga en cuenta que nuestra función main() se ha definido con async para permitir llamar al método asíncrono SystemChrome.setPrefferedOrientations(), que bloquea la orientación del dispositivo a DeviceOrientation.portraitUp. Esto se eligió solo por conveniencia y se pudo modificar para cualquier propósito.

Ahora echemos un vistazo al AppContainer, en components/app-container.dart:

La clase AppContainer con Background y NavContainer apilados en el eje z

Esta clase también extiende StatelessWidget ya que es solo un contenedor sin estado propio. Toma el parámetro de pantalla que es nuestra lista de letras A a E. Observe que el build devuelve WillPopScope, que permite que este widget capture el botón de retroceso de Android y luego llama a NavigationBus dentro del controlador onWillPop para reenviar eventos en sentido ascendente.

Fondo desplazable

La siguiente clase de componentes que veremos es el widget Background, que se encuentra en lib/components/background.dart:

La clase Background con varias propiedades e inicializadores

A diferencia de los archivos de clase que examinamos anteriormente, esta clase Background extiende un StatefulWidget para admitir propiedades de estado mutables locales y la capacidad de los eventos para desencadenar un redibujado utilizando setState(). Hay una propiedad aspectRatio que he establecido arbitrariamente en 16/9, pero podría ser cualquier relación de aspecto dependiendo de las imágenes que elija visualizar y los tamaños y orientaciones del dispositivo que pretende admitir en su aplicación. Además, hay un método initState() que anula un listener de eventos en el mismo NavigationBus que se mostró en la última clase. El propósito de este listener es hacer que este widget se vuelva a dibujar cuando un nuevo objeto Animation esté disponible en el bus de eventos. Dado que la animación de la pestaña de desplazamiento no es probable que exista en el momento en que se crea el Fondo por primera vez, esto garantiza que recibirá una referencia a la Animación actualizada una vez que haya sido creada por el controlador de la pestaña de desplazamiento.

La clase Background mostrando el método build

El método build para la clase Background toma una referencia a la instancia actual de Animation disponible en el NavController, la pasa al AnimatedBuilder y luego calcula un offset para Alignment que es el 10% del valor de la animación. En este ejemplo, con cinco pantallas, el valor para animation.value estará en el rango de 0.0 a 4.0, que a su vez mapeamos de 0.0 a 0.4 para pasar al widget Alignment, que usa este valor como un porcentaje de offset para compensar el child de OverflowBox. Esto funciona muy bien para nuestro proyecto de ejemplo, pero en las aplicaciones del mundo real, el posicionamiento real de la imagen dependerá de su tamaño y del aspecto utilizado para el efecto parallax.

Navigation Event Bus

El siguiente es nuestro único archivo de servicio, services/navigation-bus.dart:

Los archivo services/navigation-bus.dart con tipos de eventos y la clase NavigationBus

Justo encima de la clase NavigationBus, hay algunas definiciones de clase de una sola línea para los tipos de eventos, que se utilizan al reenviar eventos a listeners específicos según el tipo de evento. Se crea una instancia de EventBus y hay un getter de animación, que devuelve una AlwaysStoppedAnimation a menos que haya una instancia de TabController disponible, en cuyo punto comenzará a devolver la animación de ese controlador, para permitir que otros widgets se salgan de la pestaña del navegador. cambiando el valor de la animación. También hay métodos para registrar nuevos oyentes. El primero de estos, registerNavigationListener(), no se usa en este proyecto, pero proporciona un ejemplo de cómo el EventBus se puede usar para rastrear eventos de navegación en toda la aplicación para manejar todo tipo de cosas, desde la activación de solicitudes de red en segundo plano hasta análisis de grabación o algo más. El otro listener de evento, registerControllerAttachedListener, es usado por la clase Background para suscribirse a eventos de tipo ControllerAttachedEvent.

Cuando se registra un TabController en el bus, se guarda una referencia al mismo y los eventos de animación del mismo se envían al bus de eventos a través del controlador onUpdateTabAnimation. Además, un ControllerAttachedEvent se dispara en el bus para notificar a los componentes relacionados que tomen una nueva copia de la Animación, como en el caso de Background que comienza solo en una posición fija, y luego comienza a seguir el navegador de pestañas una vez que todo está en funcionamiento.

Navigation Container UX

Los componentes de navegación se dividen en tres archivos: el contenedor principal, un widget para el contenido de la pantalla y un widget para los iconos de navegación de pestañas. Comenzaremos con el archivo de navegación principal, components/nav-container.dart:

La clase NavContainer con varias propiedades state

NavContainer sirve como contenedor principal para mostrar tanto el contenido de la aplicación como las pestañas inferiores. Hay una propiedad children y una referencia local para TabController y Animation. El getter _tabs comienza con la propiedad children (letras A hasta E) y la asigna a un List<Tab> con instancias de NavTabIcon, que resalta la pestaña seleccionada actualmente.

La clase NavContainer mostrando _routes and various overrides

También dentro de esta clase está un getter que toma la misma propiedad children y mapea a un List<NavRouteView> que muestra nuestras letras A a la E y aplica algunos efectos de navegación durante las transiciones de página. El override de initState() crea un TabController y lo registra en el NavigationBus. Además, existe un método dispose() que elimina explícitamente la instancia del controlador cuando se desecha el contenedor de navegación.

La clase NavContainer con método build

No profundizaremos mucho en esta clase, pero es una implementación bastante estándar de las columnas, contenedores y decoraciones habituales. Existen algunas ramas condicionales para manejar la representación de la barra de pestañas de manera diferente en iOS que en Android, lo que ayuda a que las pestañas salgan de las esquinas redondeadas y el manejador drawer inferior en el iPhone X, que normalmente interferiría.

El siguiente es el archivo lib/components/nav-route-view.dart:

El widget animated NavRouteView con algunos cálculos

Aquí tenemos nuestro NavRouteView, que es un AnimatedWidget que aplica varias transformaciones al route cuando entra y sale de la pantalla. El método scaleDouble es una transformación lineal que toma el valor de la animación y lo reduce proporcionalmente a un valor más pequeño, en este caso al 80% del original. Observe la fórmula para calcular scale que determina cómo se dibujará el contenedor en la pantalla dependiendo de si se está deslizando hacia dentro o fuera de la vista, y dónde está en relación con la parte visible de la pantalla, que se maneja restando el índice del widget a partir del valor de la animación y luego realizar un grupo de pruebas condicionales en el resultado.

Por último, pero no menos importante, echaremos un vistazo a lib/components/nav-tab-icon.dart:

NavTabIcon con cálculos para renderizar un ícono

La clase NavTabIcon es similar a NavRouteView en que actualiza el aspecto que debería tener en función del estado actual de la animación que se le haya transmitido (nuestro valor de animación del navegador de pestañas). El código que logra esto se podría convertir en una característica abstracta en lugar de duplicarse entre estas clases, pero esto brinda cierta flexibilidad para experimentar con diferentes valores para cada una.

Este widget representa el icono proporcionado, que en este ejemplo es simplemente una de las mismas letras A a E con un estilo de texto aplicado.

Nuevamente, vemos una comprobación de la plataforma para ayudar a aliviar cualquier problema que surja al dibujar sobre los componentes nativos del teléfono iOS. Los valores de opacidad se fijan entre 0.26 y 0.84 en este widget, para evitar que los iconos sean totalmente opacos o transparentes, dándoles un aspecto más sutil.

Conclusión

Flutter proporciona un poderoso conjunto de características que aprovechan al máximo el lenguaje Dart para crear aplicaciones nativas modernas, con increíbles animaciones y transiciones que se reproducen a la perfección con un excelente rendimiento.

Este proyecto ilustra algunos de los conceptos dentro de Flutter que hacen posible crear aplicaciones móviles altamente complejas e interactivas con archivos de origen limpio, libres de listas gigantes de dependencias y código de espagueti.

Al utilizar un Bus de eventos y algunos componentes estándar, pudimos conectar lo que de otro modo requeriría otro paquete de dependencia, de una manera que es fácil de mantener y es poco probable que se rompa sin una razón aparente cuando menos lo espera. Estos conceptos se pueden aplicar a cualquier tipo de animación o gesto de control que se pueda imaginar, limitado solo por la imaginación.

Espero que hayan disfrutado este artículo y lo hayan encontrado útil. El código fuente completo del proyecto está disponible aquí, y no dude en contactarme con cualquier pregunta o sugerencia.

Gracias por leer y buena suerte con Flutter!

Los agradecimientos y aplausos son para el autor original del post.

--

--