Dirección del Compass con AnimatedRotation en Flutter

Rene Lazo
Flutter España
Published in
3 min readJun 15, 2022

Cree una transición de ángulo suave para los usuarios en su aplicación de mapas

Actualmente estoy trabajando en un proyecto que involucra integración de mapas. Uno de los requisitos era actualizar, no solo la posición, sino también hacia dónde apunta el usuario.

Para hacer esto, integré los siguientes plugins a la aplicación:

  • flutter_compass: Brújula de Flutter. El rumbo varía de 0 a 360, siendo 0 el norte.
  • flutter_map: Paquete de mapeo versátil para Flutter, basado en leaflet.js, simple, fácil de aprender, completamente personalizable y configurable.

Para obtener la documentación completa sobre el complemento flutter_compass, consulte la página de ejemplo en pub.dev.

Para obtener los cambios continuos en el encabezado del usuario, debemos suscribirnos a la API proporcionada por el plugin flutter_compass de la siguiente manera:

double? userHeading;

FlutterCompass.events?.listen((CompassEvent event) {
userHeading = event.heading;
}

Si la dirección(userHeading) es nula, significa que el cliente no tiene el sensor de brújula en su dispositivo. Este escenario debe manejarse para no tener excepciones en la ejecución de nuestra aplicación.

Para exponer el valor de dirección, se podrían aplicar varias soluciones:

  • StreamBuilder
  • Provider
  • ValueNotifier
  • etc.

Pero este no es el tema principal del post actual, así que solo usa el que prefieras ;)

Para el widget, la página de ejemplo del plugin nos sugiere usar Transform.rotate de la siguiente manera:

Transform.rotate(
angle: (direction * (math.pi / 180) * -1),
child: Image.asset('assets/compass.jpg'),
)

Aunque funciona, he encontrado otra forma de hacer que los cambios de ángulo sean mas “suaves”.

¿Que otra opcion tenemos?

AnimatedRotation

Versión animada de Transform.rotate que cambia automáticamente la rotación del hijo durante una duración determinada cada vez que cambia la rotación dada.

Pero este widget requiere un valor de giro(turn) para compararlo con su valor anterior y luego animar la transición de la rotación, de izquierda a derecha (si el valor aumenta) o de derecha a izquierda (si el valor disminuye).

Transformar el ángulo en giros

Ok, genial, esto es fácil, ¿verdad? Simplemente divida la dirección / 360 para obtener un valor basado en el lugar al que apunta el usuario.

Pero ahora nos enfrentaremos al primer GRAN problema:
La brújula se comportará mal en dos escenarios:

  • Si la rotación del usuario es de 1 grado a 360.
  • Si la rotación del usuario es de 360 grados a 1.

Esta es la parte complicada

Porque necesitaremos hacer algunos cálculos para obtener la variación del ángulo y entonces obtener el comportamiento esperado.

Actualicemos nuestra función de escucha de esta manera:

double turns = 0;
double prevValue = 0;

FlutterCompass.events?.listen((CompassEvent event) {
double? direction = event.heading;

direction = direction < 0 ? (360 + direction): direction;
double diff = direction - prevValue;
if(diff.abs() > 180) {
if(prevValue > direction) {
diff = 360 - (direction-prevValue).abs();
} else {
diff = 360 - (prevValue-direction).abs();
diff = diff * -1;
}
}
turns += (diff / 360);
prevValue = direction;
}

Ahora tenemos en turns el valor deseado, solo el cambio entre el último y el nuevo ángulo.

El widget debería verse así:

AnimatedRotation(
turns: turns,
duration: const Duration(milliseconds: 250),
child: const Icon(Icons.arrow_upward_outlined),
)

Espero que esto les ayude y ahorre algo de tiempo ;)

--

--

Rene Lazo
Flutter España

Tech enthusiast, loves automation solutions for everyday problems, Flutter, UI and UX