DEV Community

Cover image for Compass Heading with AnimatedRotation in Flutter
Rene Lazo Mendez
Rene Lazo Mendez

Posted on

Compass Heading with AnimatedRotation in Flutter

Create a smooth angle transition for the users in your map application

So, I'm working on a project that involves a map. One of the requirements was to update not only the position, but also were the user points to.

To do this, I integrated the following plugins into the app:

  • flutter_compass - A Flutter compass. The heading varies from 0-360, 0 being north.
  • flutter_map - A versatile mapping package for Flutter, based off leaflet.js, that's simple and easy to learn, yet completely customizable and configurable.

For full documentation on the flutter_compass plugin, see the the example page on pub.dev.

Now, to get the continuous changes in the user's heading we need to subscribe to the API provided by the flutter_compass plugin:

double? userHeading;

FlutterCompass.events?.listen((CompassEvent event) {
  userHeading = event.heading;
}
Enter fullscreen mode Exit fullscreen mode

If direction is null this means the client does not have the compass sensor on their device, and this scenario must be handled so as not to have exceptions in the execution of our app.

To expose this direction value, you could have several approaches:

  • StreamBuilder
  • Provider
  • ValueNotifier
  • etc.

But this is not the main topic, so just use your preferred one ;)

For the widget, the plugin's example suggest us to use Transform.rotate as follows:

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

Even though it works, I have found another way to make the angle changes in a smooth way.

Which other option do we have???

AnimatedRotation
Animated version of Transform.rotate which automatically transitions the child's rotation over a given duration whenever the given rotation changes.

But this widget requires a turn value to check against its previous value and then animate the transition of the rotation, left to right (if the value increases) or right to left (if the value decreases).

Transform the angle into turns

Ok cool, this is easy right? Simply divide the direction / 360 to get a value based on where the user is pointing.

But now we will face the first BIG issue:
The compass will misbehave in two scenarios:

  • If the user's rotation it's from 1 degree to 360.
  • If the user's rotation it's from 360 degrees to 1.

This is the tricky part

Because we will need to do some math to just get the variation of the angle and then get the expected behavior.

Lets upgrade our listener function like this:

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;
}
Enter fullscreen mode Exit fullscreen mode

We now have in turns the desire value, just the change between the last and the new angle.

The widget should look like this:

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

I hope this helps you and saves you some time ;)

Top comments (0)