Dirección del Compass con AnimatedRotation en Flutter
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?
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 ;)