import 'package:audioplayers/audioplayers.dart'; import 'package:flutter/services.dart'; import 'package:vibration/vibration.dart'; class FeedbackService { FeedbackService() : _player = AudioPlayer() { _player.setReleaseMode(ReleaseMode.stop); } final AudioPlayer _player; Future onCodeDetected() async { await Future.wait([ _play('sounds/scan.wav'), _vibratePattern(const [0, 40]), ]); } Future onSuccess() async { await Future.wait([ _play('sounds/success.wav'), _vibratePattern(const [0, 90, 70, 140]), ]); await HapticFeedback.heavyImpact(); } Future onWarning() async { await Future.wait([ _play('sounds/warning.wav'), _vibratePattern(const [0, 120, 80, 120, 80, 120]), ]); await HapticFeedback.mediumImpact(); } Future _play(String asset) async { try { await _player.stop(); await _player.play(AssetSource(asset)); } catch (_) { // Ignore audio errors on devices without speaker routing. } } Future _vibratePattern(List pattern) async { try { final hasVibrator = await Vibration.hasVibrator(); if (hasVibrator != true) return; final hasAmplitude = await Vibration.hasAmplitudeControl(); if (hasAmplitude == true) { final intensities = pattern .asMap() .entries .map((entry) => entry.key.isOdd ? 180 : 0) .toList(); await Vibration.vibrate( pattern: pattern, intensities: intensities, ); return; } await Vibration.vibrate(pattern: pattern); } catch (_) { await HapticFeedback.selectionClick(); } } Future dispose() async { await _player.dispose(); } }