The flutter_rotation_sensor plugin provides easy access to the device's physical orientation on
Android, iOS, and supported web browsers in three distinct representations: rotation matrix,
quaternion, and Euler angles (azimuth, pitch, roll). This is ideal for applications requiring
precise tracking of the device's movement or orientation in space, such as augmented reality,
gaming, navigation, and more.
- Real-time Rotation Data: Access to real-time rotation data.
- Multiple Formats Supported: Provides rotation matrix, quaternion, and Euler angles (azimuth, pitch, roll).
- Customizable Update Intervals: Set custom intervals for sensor data retrieval.
- Coordinate System Remapping: Supports orientation coordinate system remapping.
- Web Support: Works in browsers with the Sensors API or
DeviceOrientationEventsupport.
To add flutter_rotation_sensor to your project, follow these steps:
-
Add
flutter_rotation_sensoras a dependency in yourpubspec.yamlfile:dependencies: flutter_rotation_sensor: ^latest_version
-
Install the plugin by running:
flutter pub get
-
Import the plugin in your Dart code:
import 'package:flutter_rotation_sensor/flutter_rotation_sensor.dart';
To start receiving orientation data from the sensors on supported platforms, simply use the stream
in a StreamBuilder:
@override
Widget build(BuildContext context) {
if (RotationSensor.isPlatformSupported) {
return StreamBuilder(
stream: RotationSensor.orientationStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
final data = snapshot.data!;
print(data.quaternion);
print(data.rotationMatrix);
print(data.eulerAngles);
// ...
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
return const CircularProgressIndicator();
}
},
);
} else {
return const Text('Rotation sensor is not supported on this platform.');
}
}Use RotationSensor.isPlatformSupported to guard unsupported browsers or devices before starting a
subscription.
For more control, you can subscribe to the stream directly:
-
Initialize the sensor and specify the desired update interval during
initState:late final StreamSubscription<OrientationEvent> orientationSubscription; @override void initState() { super.initState(); orientationSubscription = RotationSensor.orientationStream.listen((event) { final azimuth = event.eulerAngles.azimuth; // Print azimuth: 0 for North, π/2 for East, π for South, 3π/2 for West print(azimuth); }); }
-
Remember to cancel the subscription in the
disposemethod to prevent memory leaks:@override void dispose() { orientationSubscription.cancel(); super.dispose(); }
To configure the flutter_rotation_sensor plugin, you can set various properties at any time, such
as in your initState method. Below is an example demonstrating how to configure these settings:
@override
void initState() {
super.initState();
// Set the sampling period for the rotation sensor
RotationSensor.samplingPeriod = SensorInterval.uiInterval;
// Set the reference frame from which the azimuth is measured
RotationSensor.referenceFrame = ReferenceFrame.trueNorth;
// Set the coordinate system for the rotation sensor
RotationSensor.coordinateSystem = CoordinateSystem.transformed(Axis3.X, Axis3.Z);
}- Android: Uses the native rotation sensor implementation.
- iOS: Uses the native rotation sensor implementation.
- Web: Uses the browser Sensors API when available, otherwise falls back to
DeviceOrientationEvent.
For web platform, permission is handled at runtime. The plugin provides the following methods to manage permissions:
RotationSensor.shouldRequestPermissiontells you whether the current browser exposes an explicit permission flow. It always returns false for Android and iOS, which do not require explicit permission for sensor access.RotationSensor.requestPermission()returnsSensorPermission.grantedorSensorPermission.denied. It must be called from a transient user activation, such as a button tap. This matches the behavior described in MDN's DeviceOrientationEvent.requestPermission() docs.
bool showPermissionButton = RotationSensor.shouldRequestPermission;
@override
Widget build() {
if (!RotationSensor.isPlatformSupported) {
return Text('Rotation sensor is not supported on this platform.');
} else if (showPermissionButton) {
return ElevatedButton(
onPressed: () async {
final result = await RotationSensor.requestPermission();
if (result == .granted) {
setState(() => showPermissionButton = false);
}
},
child: const Text('Start'),
);
} else {
// ...
}
}If permission is denied or the browser blocks sensor access, check the following:
- The page is served over HTTPS or
localhost. - The browser allows motion/orientation sensors.
- The site is not blocked by a Permissions Policy / feature policy.
- The device actually has the required sensors enabled.
The RotationSensor.samplingPeriod determines how frequently the sensor data is updated. Here are the predefined values you can use:
SensorInterval.normalInterval(200ms): Default rate, suitable for general use.SensorInterval.uiInterval(66ms): Suitable for UI updates, balancing update rate and power consumption.SensorInterval.gameInterval(20ms): Suitable for games, updating at a rate to ensure smooth motion.SensorInterval.fastestInterval(0ms): Updates as fast as possible.
You can also set a custom Duration, for example:
void config() {
RotationSensor.samplingPeriod = Duration(seconds: 1);
}Events may arrive at a rate faster or slower than the sampling period, which is only a hint to the system. The actual rate depends on the system's event queue and sensor hardware capabilities.
The RotationSensor.referenceFrame property controls the world reference from which the angles are measured. Here are the values you can use:
ReferenceFrame.arbitrary: Uses the initial device orientation as the frame of reference.ReferenceFrame.arbitraryCorrected: Uses the magnetometer to improve long-term accuracy.ReferenceFrame.magneticNorth: (default value) Points to the magnetic north pole.ReferenceFrame.trueNorth: Points to the geographic north pole.
void config() {
RotationSensor.referenceFrame = ReferenceFrame.trueNorth;
}The RotationSensor.coordinateSystem property allows you to remap the coordinate system used by the sensor data. By default, the coordinate system follows the display's orientation. You can transform the coordinate system to match your application's needs. Here are the predefined coordinate systems you can use:
CoordinateSystem.device(): Defined relative to the device's screen in its default orientation.CoordinateSystem.display(): (default value) Adapts to the device's current orientation.CoordinateSystem.transformed(): Applies a transformation on top of a base coordinate system.
For example, a driving navigation application may want a transformed coordinate system where the y-axis points to the back of the device. This ensures that the plugin can return the azimuth correctly when the device is mounted in front of the driver.
void config() {
// The new x-axis is same as old x-axis and the new y-axis is the old negative-z-axis which points
// to the back of the device.
RotationSensor.coordinateSystem = CoordinateSystem.transformed(Axis3.X, -Axis3.Z);
}Some browsers require an explicit user gesture before they allow access to motion or orientation
sensors. In those cases, call RotationSensor.requestPermission() from a button tap or similar
interaction.
You can usually start listening directly. The browser either does not require a prompt or does not support the permission API.
Check browser support, HTTPS, Permissions Policy settings, and whether the device exposes the needed sensors.
It lets you avoid starting a stream on unsupported browsers or devices and show a fallback UI instead.
This plugin is licensed under the MIT License.