import 'dart:async' as async; import 'dart:typed_data'; import 'package:audioplayers/audioplayers.dart'; abstract class RuntimeAudioPlayer { Future start( Uint8List bytes, { required double volume, bool loop = false, }); Future pause(); Future resume(); Future stop(); Future get done; Future dispose(); } class AudioplayersRuntimeAudioPlayer implements RuntimeAudioPlayer { AudioplayersRuntimeAudioPlayer({AudioPlayer? player}) : _player = player ?? AudioPlayer(); final AudioPlayer _player; async.Completer _done = async.Completer(); async.StreamSubscription? _completionSubscription; bool _disposed = false; @override Future get done => _done.future; @override Future start( Uint8List bytes, { required double volume, bool loop = false, }) async { if (_disposed) { return; } await _completionSubscription?.cancel(); _completionSubscription = null; if (_done.isCompleted) { _done = async.Completer(); } _completionSubscription = _player.onPlayerComplete.listen((_) { _completeDone(); }); await _player.setReleaseMode(loop ? ReleaseMode.loop : ReleaseMode.release); await _player.play(BytesSource(bytes), volume: volume); } @override Future pause() async { if (_disposed) { return; } await _player.pause(); } @override Future resume() async { if (_disposed) { return; } await _player.resume(); } @override Future stop() async { if (_disposed) { return; } await _player.stop(); _completeDone(); } @override Future dispose() async { if (_disposed) { return; } _disposed = true; await _completionSubscription?.cancel(); _completionSubscription = null; await _player.dispose(); _completeDone(); } void _completeDone() { if (!_done.isCompleted) { _done.complete(); } } }