import 'package:audioplayers/audioplayers.dart'; enum Sfx { place, clear, combo, win, lose } /// Fire-and-forget SFX. A small player pool avoids cutting sounds off when /// effects overlap (place + clear on the same move). class AudioService { AudioService({this.enabled = true}); bool enabled; static const _files = { Sfx.place: 'audio/place.wav', Sfx.clear: 'audio/clear.wav', Sfx.combo: 'audio/combo.wav', Sfx.win: 'audio/win.wav', Sfx.lose: 'audio/lose.wav', }; final _players = [ for (var i = 0; i < 3; i++) AudioPlayer()..setReleaseMode(ReleaseMode.stop), ]; var _next = 0; Future play(Sfx sfx) async { if (!enabled) return; final player = _players[_next]; _next = (_next + 1) % _players.length; try { await player.stop(); await player.play(AssetSource(_files[sfx]!)); } catch (_) { // Audio must never break gameplay. } } void dispose() { for (final p in _players) { p.dispose(); } } }