import 'dart:async'; import 'dart:math' as math; import 'package:sensors_plus/sensors_plus.dart'; class TiltDetector { static final TiltDetector _instance = TiltDetector._internal(); factory TiltDetector() => _instance; TiltDetector._internal(); StreamSubscription? _accelerometerSubscription; Function(double)? onTiltChanged; bool _isListening = false; double _currentTilt = 0.0; // Filtering parameters to smooth out noise static const double _alpha = 0.8; // Low-pass filter constant double _filteredX = 0.0; bool get isListening => _isListening; double get currentTilt => _currentTilt; /// Start listening to accelerometer data /// [onTiltChanged] callback receives tilt angle in degrees /// Positive values = device tilted right, negative = tilted left void startListening({Function(double)? onTiltChanged}) { if (_isListening) return; this.onTiltChanged = onTiltChanged; _isListening = true; _accelerometerSubscription = accelerometerEventStream( samplingPeriod: const Duration(milliseconds: 100), // 10 Hz sampling rate ).listen(_onAccelerometerEvent); } /// Stop listening to accelerometer data void stopListening() { if (!_isListening) return; _accelerometerSubscription?.cancel(); _accelerometerSubscription = null; _isListening = false; onTiltChanged = null; _currentTilt = 0.0; _filteredX = 0.0; } void _onAccelerometerEvent(AccelerometerEvent event) { // Apply low-pass filter to smooth out noise _filteredX = _alpha * _filteredX + (1 - _alpha) * event.x; // Calculate tilt angle in degrees // The accelerometer X axis represents left-right tilt // Clamp to reasonable tilt range (-45 to +45 degrees) final tiltRadians = math.atan2(_filteredX, 9.8); _currentTilt = (tiltRadians * 180 / math.pi).clamp(-45.0, 45.0); // Only trigger callback if tilt is significant (> 5 degrees) if (_currentTilt.abs() > 5.0) { onTiltChanged?.call(_currentTilt); } } /// Get normalized tilt strength from -1.0 to 1.0 /// -1.0 = maximum left tilt, +1.0 = maximum right tilt double get normalizedTilt { return (_currentTilt / 45.0).clamp(-1.0, 1.0); } /// Check if device is significantly tilted bool get isTilted => _currentTilt.abs() > 5.0; /// Check if device is tilted left bool get isTiltedLeft => _currentTilt < -5.0; /// Check if device is tilted right bool get isTiltedRight => _currentTilt > 5.0; }