tilt_detector.dart 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. import 'dart:async';
  2. import 'dart:math' as math;
  3. import 'package:sensors_plus/sensors_plus.dart';
  4. class TiltDetector {
  5. static final TiltDetector _instance = TiltDetector._internal();
  6. factory TiltDetector() => _instance;
  7. TiltDetector._internal();
  8. StreamSubscription<AccelerometerEvent>? _accelerometerSubscription;
  9. Function(double)? onTiltChanged;
  10. bool _isListening = false;
  11. double _currentTilt = 0.0;
  12. // Filtering parameters to smooth out noise
  13. static const double _alpha = 0.8; // Low-pass filter constant
  14. double _filteredX = 0.0;
  15. bool get isListening => _isListening;
  16. double get currentTilt => _currentTilt;
  17. /// Start listening to accelerometer data
  18. /// [onTiltChanged] callback receives tilt angle in degrees
  19. /// Positive values = device tilted right, negative = tilted left
  20. void startListening({Function(double)? onTiltChanged}) {
  21. if (_isListening) return;
  22. this.onTiltChanged = onTiltChanged;
  23. _isListening = true;
  24. _accelerometerSubscription = accelerometerEventStream(
  25. samplingPeriod: const Duration(milliseconds: 200), // 5 Hz sampling rate for better performance
  26. ).listen(_onAccelerometerEvent);
  27. }
  28. /// Stop listening to accelerometer data
  29. void stopListening() {
  30. if (!_isListening) return;
  31. _accelerometerSubscription?.cancel();
  32. _accelerometerSubscription = null;
  33. _isListening = false;
  34. onTiltChanged = null;
  35. _currentTilt = 0.0;
  36. _filteredX = 0.0;
  37. }
  38. void _onAccelerometerEvent(AccelerometerEvent event) {
  39. // Apply low-pass filter to smooth out noise
  40. _filteredX = _alpha * _filteredX + (1 - _alpha) * event.x;
  41. // Calculate tilt angle in degrees
  42. // The accelerometer X axis represents left-right tilt
  43. // Clamp to reasonable tilt range (-45 to +45 degrees)
  44. final tiltRadians = math.atan2(_filteredX, 9.8);
  45. _currentTilt = (tiltRadians * 180 / math.pi).clamp(-45.0, 45.0);
  46. // Only trigger callback if tilt is significant (> 5 degrees)
  47. if (_currentTilt.abs() > 5.0) {
  48. onTiltChanged?.call(_currentTilt);
  49. }
  50. }
  51. /// Get normalized tilt strength from -1.0 to 1.0
  52. /// -1.0 = maximum left tilt, +1.0 = maximum right tilt
  53. double get normalizedTilt {
  54. return (_currentTilt / 45.0).clamp(-1.0, 1.0);
  55. }
  56. /// Check if device is significantly tilted
  57. bool get isTilted => _currentTilt.abs() > 5.0;
  58. /// Check if device is tilted left
  59. bool get isTiltedLeft => _currentTilt < -5.0;
  60. /// Check if device is tilted right
  61. bool get isTiltedRight => _currentTilt > 5.0;
  62. }