diff --git a/lib/core/services/audio_service.dart b/lib/core/services/audio_service.dart index 09d62a8..f6a0f03 100644 --- a/lib/core/services/audio_service.dart +++ b/lib/core/services/audio_service.dart @@ -30,12 +30,13 @@ class AudioService { } } - Future play() async { + Future play({bool waitUntilComplete = false}) async { try { await _player.play(); - await _player.processingStateStream - .firstWhere((s) => s == ProcessingState.completed); - return; + if (!waitUntilComplete) return; + await _player.processingStateStream.firstWhere( + (s) => s == ProcessingState.completed, + ); } catch (e) { if (kDebugMode) { print('$e'); @@ -65,6 +66,7 @@ class AudioService { Future dispose() async { try { + await _streamController.close(); await _player.dispose(); } catch (e) { if (kDebugMode) { diff --git a/lib/features/question/presentation/bloc/question_bloc.dart b/lib/features/question/presentation/bloc/question_bloc.dart index 57633a7..0e45d8a 100644 --- a/lib/features/question/presentation/bloc/question_bloc.dart +++ b/lib/features/question/presentation/bloc/question_bloc.dart @@ -40,6 +40,7 @@ class QuestionBloc extends Bloc { @override Future close() { + _sequenceToken++; if (!_mainAudioService.isMuted) { _mainAudioService.setVolume(volume: MyConstants.musicAudioVolume); } @@ -56,19 +57,19 @@ class QuestionBloc extends Bloc { final GetNextLevelUseCase _getNextLevelUseCase; /// ------------Variables------------ - final Map showCaseKey = { - 'answer_key_0': GlobalKey(), - 'answer_key_1': GlobalKey(), - 'answer_key_2': GlobalKey(), - 'answer_key_3': GlobalKey(), - 'notif_key_0': GlobalKey(), - 'notif_key_1': GlobalKey(), - 'notif_key_2': GlobalKey(), - 'notif_key_3': GlobalKey(), - 'stepper_key': GlobalKey(), - 'hadith_key': GlobalKey(), - 'guide_key': GlobalKey(), - }; + // final Map showCaseKey = { + // 'answer_key_0': GlobalKey(), + // 'answer_key_1': GlobalKey(), + // 'answer_key_2': GlobalKey(), + // 'answer_key_3': GlobalKey(), + // 'notif_key_0': GlobalKey(), + // 'notif_key_1': GlobalKey(), + // 'notif_key_2': GlobalKey(), + // 'notif_key_3': GlobalKey(), + // 'stepper_key': GlobalKey(), + // 'hadith_key': GlobalKey(), + // 'guide_key': GlobalKey(), + // }; final Map globeStates = { MyAnimations.globeStateSpeaking: 0, MyAnimations.globeStateNormal: 0.25, @@ -86,6 +87,7 @@ class QuestionBloc extends Bloc { Stream? volumeStream; bool showAnswerSequence = true; bool isHadithDialogOpen = false; + int _sequenceToken = 0; /// ------------Controllers------------ final AudioService _mainAudioService; @@ -96,6 +98,10 @@ class QuestionBloc extends Bloc { AnimationController? globeAnimationController; final ScrollController scrollController = ScrollController(); + int _nextSequenceToken() => ++_sequenceToken; + + bool _isActiveSequence(int token) => !isClosed && token == _sequenceToken; + /// ------------Functions------------ // void startScrollTitle({Duration? audioDuration}) { // if (audioDuration == null || audioDuration == Duration.zero) return; @@ -125,12 +131,12 @@ class QuestionBloc extends Bloc { Future playDiamondAudio() async { await _mainAudioService.setAudio(assetPath: MyAudios.diamondEnd); - await _mainAudioService.play(); + await _mainAudioService.play(waitUntilComplete: true); } Future playRightAnswerAudio() async { await _mainAudioService.setAudio(assetPath: MyAudios.rightAnswer); - await _mainAudioService.play(); + await _mainAudioService.play(waitUntilComplete: true); } Future initAudios() async { @@ -154,17 +160,17 @@ class QuestionBloc extends Bloc { debugPrint('playWrongAudio: $audio'); await _mainAudioService.setAudio(assetPath: audio); - await _mainAudioService.play(); + await _mainAudioService.play(waitUntilComplete: true); } Future playAnswerAudio({String? audio}) async { await _mainAudioService.setAudio(filePath: audio); - await _mainAudioService.play(); + await _mainAudioService.play(waitUntilComplete: true); } Future playQuestionAudio() async { await _mainAudioService.setAudio(filePath: state.currentQuestion?.audio); - await _mainAudioService.play(); + await _mainAudioService.play(waitUntilComplete: true); } Future changeMute() async { @@ -177,13 +183,17 @@ class QuestionBloc extends Bloc { Future showQueueAnswer() async { if (!showAnswerSequence) return; - final List answers = state.currentQuestion?.answers ?? []; + final int token = _sequenceToken; + final List answers = List.from( + state.currentQuestion?.answers ?? const [], + ); if (answers.isNotEmpty) { answers.removeWhere((e) => e.imageId == null); } for (final answer in answers) { + if (!_isActiveSequence(token)) return; await Future.delayed(const Duration(milliseconds: 350), () async { - if (MyContext.get.mounted) { + if (_isActiveSequence(token) && MyContext.get.mounted) { await showAnswerDialog( context: MyContext.get, answerEntity: answer, @@ -192,6 +202,7 @@ class QuestionBloc extends Bloc { } }); } + if (!_isActiveSequence(token)) return; changeGlobeState(key: MyAnimations.globeStateNormal); } @@ -264,10 +275,12 @@ class QuestionBloc extends Bloc { GetLevelEvent event, Emitter emit, ) async { + final int token = _nextSequenceToken(); await _getLevelUseCase(QuestionParams(id: int.parse(event.id ?? '0'))).then( (value) { value.fold( (data) async { + if (!_isActiveSequence(token)) return; final LevelEntity level = LevelEntity( id: data.id, order: data.order, @@ -286,11 +299,14 @@ class QuestionBloc extends Bloc { correctAnswer: false, ), ); + if (!_isActiveSequence(token)) return; imageAnimationController?.forward(); changeGlobeState(key: MyAnimations.globeStateSpeaking); await playQuestionAudio(); + if (!_isActiveSequence(token)) return; imageAnimationController?.reverse(); answerAnimationController?.forward().then((value) { + if (!_isActiveSequence(token)) return; showQueueAnswer(); }); }, @@ -308,6 +324,7 @@ class QuestionBloc extends Bloc { ChooseAnswerEvent event, Emitter emit, ) async { + final int token = _nextSequenceToken(); emit(state.copyWith(correctAnswer: event.chooseCorrectAnswer)); if (event.chooseCorrectAnswer) { @@ -322,9 +339,11 @@ class QuestionBloc extends Bloc { AnswerEntity(), showConfetti: true, ); + if (!_isActiveSequence(token)) return; scrollController.jumpTo(0); answerAnimationController?.reverse(); await Future.delayed(const Duration(seconds: 1), () async { + if (!_isActiveSequence(token)) return; final QuestionEntity? findPreQuestion = state.currentQuestion; final int findIndex = (findPreQuestion?.order ?? 1); final newCurrentQuestion = state.levelEntity?.questions?[findIndex]; @@ -335,6 +354,7 @@ class QuestionBloc extends Bloc { await Future.delayed(const Duration(milliseconds: 400)); } emit(state.copyWith(currentQuestion: newCurrentQuestion)); + if (!_isActiveSequence(token)) return; if (isLastQuestion) { int currentLevel = int.parse( LocalStorage.readData(key: MyConstants.currentLevel) ?? '1', @@ -349,12 +369,15 @@ class QuestionBloc extends Bloc { } else { showingAnswerSequence(show: true); await Future.delayed(const Duration(seconds: 1)); + if (!_isActiveSequence(token)) return; imageAnimationController?.forward(); scrollController.jumpTo(0); changeGlobeState(key: MyAnimations.globeStateSpeaking); await playQuestionAudio(); + if (!_isActiveSequence(token)) return; imageAnimationController?.reverse(); answerAnimationController?.forward().then((value) { + if (!_isActiveSequence(token)) return; showQueueAnswer(); }); } diff --git a/lib/features/question/presentation/ui/screens/answer_screen.dart b/lib/features/question/presentation/ui/screens/answer_screen.dart index 34a9db8..8fd4279 100644 --- a/lib/features/question/presentation/ui/screens/answer_screen.dart +++ b/lib/features/question/presentation/ui/screens/answer_screen.dart @@ -63,12 +63,12 @@ class _AnswerScreenState extends State { Future playCorrectAudio() async { await audioService.setAudio(filePath: widget.correctAudio); - await audioService.play(); + await audioService.play(waitUntilComplete: true); } Future playAudio() async { await audioService.setAudio(filePath: widget.answerEntity.audio); - await audioService.play(); + await audioService.play(waitUntilComplete: true); } @override