Browse Source

optimization

develop
sina 2 weeks ago
parent
commit
8f08d8ded0
  1. 10
      lib/core/services/audio_service.dart
  2. 63
      lib/features/question/presentation/bloc/question_bloc.dart
  3. 4
      lib/features/question/presentation/ui/screens/answer_screen.dart

10
lib/core/services/audio_service.dart

@ -30,12 +30,13 @@ class AudioService {
} }
} }
Future<void> play() async {
Future<void> play({bool waitUntilComplete = false}) async {
try { try {
await _player.play(); 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) { } catch (e) {
if (kDebugMode) { if (kDebugMode) {
print('$e'); print('$e');
@ -65,6 +66,7 @@ class AudioService {
Future<void> dispose() async { Future<void> dispose() async {
try { try {
await _streamController.close();
await _player.dispose(); await _player.dispose();
} catch (e) { } catch (e) {
if (kDebugMode) { if (kDebugMode) {

63
lib/features/question/presentation/bloc/question_bloc.dart

@ -40,6 +40,7 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
@override @override
Future<void> close() { Future<void> close() {
_sequenceToken++;
if (!_mainAudioService.isMuted) { if (!_mainAudioService.isMuted) {
_mainAudioService.setVolume(volume: MyConstants.musicAudioVolume); _mainAudioService.setVolume(volume: MyConstants.musicAudioVolume);
} }
@ -56,19 +57,19 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
final GetNextLevelUseCase _getNextLevelUseCase; final GetNextLevelUseCase _getNextLevelUseCase;
/// ------------Variables------------ /// ------------Variables------------
final Map<String, GlobalKey> 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<String, GlobalKey> 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<String, double> globeStates = { final Map<String, double> globeStates = {
MyAnimations.globeStateSpeaking: 0, MyAnimations.globeStateSpeaking: 0,
MyAnimations.globeStateNormal: 0.25, MyAnimations.globeStateNormal: 0.25,
@ -86,6 +87,7 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
Stream<double>? volumeStream; Stream<double>? volumeStream;
bool showAnswerSequence = true; bool showAnswerSequence = true;
bool isHadithDialogOpen = false; bool isHadithDialogOpen = false;
int _sequenceToken = 0;
/// ------------Controllers------------ /// ------------Controllers------------
final AudioService _mainAudioService; final AudioService _mainAudioService;
@ -96,6 +98,10 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
AnimationController? globeAnimationController; AnimationController? globeAnimationController;
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
int _nextSequenceToken() => ++_sequenceToken;
bool _isActiveSequence(int token) => !isClosed && token == _sequenceToken;
/// ------------Functions------------ /// ------------Functions------------
// void startScrollTitle({Duration? audioDuration}) { // void startScrollTitle({Duration? audioDuration}) {
// if (audioDuration == null || audioDuration == Duration.zero) return; // if (audioDuration == null || audioDuration == Duration.zero) return;
@ -125,12 +131,12 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
Future<void> playDiamondAudio() async { Future<void> playDiamondAudio() async {
await _mainAudioService.setAudio(assetPath: MyAudios.diamondEnd); await _mainAudioService.setAudio(assetPath: MyAudios.diamondEnd);
await _mainAudioService.play();
await _mainAudioService.play(waitUntilComplete: true);
} }
Future<void> playRightAnswerAudio() async { Future<void> playRightAnswerAudio() async {
await _mainAudioService.setAudio(assetPath: MyAudios.rightAnswer); await _mainAudioService.setAudio(assetPath: MyAudios.rightAnswer);
await _mainAudioService.play();
await _mainAudioService.play(waitUntilComplete: true);
} }
Future<void> initAudios() async { Future<void> initAudios() async {
@ -154,17 +160,17 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
debugPrint('playWrongAudio: $audio'); debugPrint('playWrongAudio: $audio');
await _mainAudioService.setAudio(assetPath: audio); await _mainAudioService.setAudio(assetPath: audio);
await _mainAudioService.play();
await _mainAudioService.play(waitUntilComplete: true);
} }
Future<void> playAnswerAudio({String? audio}) async { Future<void> playAnswerAudio({String? audio}) async {
await _mainAudioService.setAudio(filePath: audio); await _mainAudioService.setAudio(filePath: audio);
await _mainAudioService.play();
await _mainAudioService.play(waitUntilComplete: true);
} }
Future<void> playQuestionAudio() async { Future<void> playQuestionAudio() async {
await _mainAudioService.setAudio(filePath: state.currentQuestion?.audio); await _mainAudioService.setAudio(filePath: state.currentQuestion?.audio);
await _mainAudioService.play();
await _mainAudioService.play(waitUntilComplete: true);
} }
Future<void> changeMute() async { Future<void> changeMute() async {
@ -177,13 +183,17 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
Future<void> showQueueAnswer() async { Future<void> showQueueAnswer() async {
if (!showAnswerSequence) return; if (!showAnswerSequence) return;
final List<AnswerEntity> answers = state.currentQuestion?.answers ?? [];
final int token = _sequenceToken;
final List<AnswerEntity> answers = List<AnswerEntity>.from(
state.currentQuestion?.answers ?? const <AnswerEntity>[],
);
if (answers.isNotEmpty) { if (answers.isNotEmpty) {
answers.removeWhere((e) => e.imageId == null); answers.removeWhere((e) => e.imageId == null);
} }
for (final answer in answers) { for (final answer in answers) {
if (!_isActiveSequence(token)) return;
await Future.delayed(const Duration(milliseconds: 350), () async { await Future.delayed(const Duration(milliseconds: 350), () async {
if (MyContext.get.mounted) {
if (_isActiveSequence(token) && MyContext.get.mounted) {
await showAnswerDialog( await showAnswerDialog(
context: MyContext.get, context: MyContext.get,
answerEntity: answer, answerEntity: answer,
@ -192,6 +202,7 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
} }
}); });
} }
if (!_isActiveSequence(token)) return;
changeGlobeState(key: MyAnimations.globeStateNormal); changeGlobeState(key: MyAnimations.globeStateNormal);
} }
@ -264,10 +275,12 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
GetLevelEvent event, GetLevelEvent event,
Emitter<QuestionState> emit, Emitter<QuestionState> emit,
) async { ) async {
final int token = _nextSequenceToken();
await _getLevelUseCase(QuestionParams(id: int.parse(event.id ?? '0'))).then( await _getLevelUseCase(QuestionParams(id: int.parse(event.id ?? '0'))).then(
(value) { (value) {
value.fold( value.fold(
(data) async { (data) async {
if (!_isActiveSequence(token)) return;
final LevelEntity level = LevelEntity( final LevelEntity level = LevelEntity(
id: data.id, id: data.id,
order: data.order, order: data.order,
@ -286,11 +299,14 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
correctAnswer: false, correctAnswer: false,
), ),
); );
if (!_isActiveSequence(token)) return;
imageAnimationController?.forward(); imageAnimationController?.forward();
changeGlobeState(key: MyAnimations.globeStateSpeaking); changeGlobeState(key: MyAnimations.globeStateSpeaking);
await playQuestionAudio(); await playQuestionAudio();
if (!_isActiveSequence(token)) return;
imageAnimationController?.reverse(); imageAnimationController?.reverse();
answerAnimationController?.forward().then((value) { answerAnimationController?.forward().then((value) {
if (!_isActiveSequence(token)) return;
showQueueAnswer(); showQueueAnswer();
}); });
}, },
@ -308,6 +324,7 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
ChooseAnswerEvent event, ChooseAnswerEvent event,
Emitter<QuestionState> emit, Emitter<QuestionState> emit,
) async { ) async {
final int token = _nextSequenceToken();
emit(state.copyWith(correctAnswer: event.chooseCorrectAnswer)); emit(state.copyWith(correctAnswer: event.chooseCorrectAnswer));
if (event.chooseCorrectAnswer) { if (event.chooseCorrectAnswer) {
@ -322,9 +339,11 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
AnswerEntity(), AnswerEntity(),
showConfetti: true, showConfetti: true,
); );
if (!_isActiveSequence(token)) return;
scrollController.jumpTo(0); scrollController.jumpTo(0);
answerAnimationController?.reverse(); answerAnimationController?.reverse();
await Future.delayed(const Duration(seconds: 1), () async { await Future.delayed(const Duration(seconds: 1), () async {
if (!_isActiveSequence(token)) return;
final QuestionEntity? findPreQuestion = state.currentQuestion; final QuestionEntity? findPreQuestion = state.currentQuestion;
final int findIndex = (findPreQuestion?.order ?? 1); final int findIndex = (findPreQuestion?.order ?? 1);
final newCurrentQuestion = state.levelEntity?.questions?[findIndex]; final newCurrentQuestion = state.levelEntity?.questions?[findIndex];
@ -335,6 +354,7 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
await Future.delayed(const Duration(milliseconds: 400)); await Future.delayed(const Duration(milliseconds: 400));
} }
emit(state.copyWith(currentQuestion: newCurrentQuestion)); emit(state.copyWith(currentQuestion: newCurrentQuestion));
if (!_isActiveSequence(token)) return;
if (isLastQuestion) { if (isLastQuestion) {
int currentLevel = int.parse( int currentLevel = int.parse(
LocalStorage.readData(key: MyConstants.currentLevel) ?? '1', LocalStorage.readData(key: MyConstants.currentLevel) ?? '1',
@ -349,12 +369,15 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
} else { } else {
showingAnswerSequence(show: true); showingAnswerSequence(show: true);
await Future.delayed(const Duration(seconds: 1)); await Future.delayed(const Duration(seconds: 1));
if (!_isActiveSequence(token)) return;
imageAnimationController?.forward(); imageAnimationController?.forward();
scrollController.jumpTo(0); scrollController.jumpTo(0);
changeGlobeState(key: MyAnimations.globeStateSpeaking); changeGlobeState(key: MyAnimations.globeStateSpeaking);
await playQuestionAudio(); await playQuestionAudio();
if (!_isActiveSequence(token)) return;
imageAnimationController?.reverse(); imageAnimationController?.reverse();
answerAnimationController?.forward().then((value) { answerAnimationController?.forward().then((value) {
if (!_isActiveSequence(token)) return;
showQueueAnswer(); showQueueAnswer();
}); });
} }

4
lib/features/question/presentation/ui/screens/answer_screen.dart

@ -63,12 +63,12 @@ class _AnswerScreenState extends State<AnswerScreen> {
Future<void> playCorrectAudio() async { Future<void> playCorrectAudio() async {
await audioService.setAudio(filePath: widget.correctAudio); await audioService.setAudio(filePath: widget.correctAudio);
await audioService.play();
await audioService.play(waitUntilComplete: true);
} }
Future<void> playAudio() async { Future<void> playAudio() async {
await audioService.setAudio(filePath: widget.answerEntity.audio); await audioService.setAudio(filePath: widget.answerEntity.audio);
await audioService.play();
await audioService.play(waitUntilComplete: true);
} }
@override @override

Loading…
Cancel
Save