You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							213 lines
						
					
					
						
							7.0 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							213 lines
						
					
					
						
							7.0 KiB
						
					
					
				| import 'dart:async'; | |
| 
 | |
| import 'package:confetti/confetti.dart'; | |
| import 'package:flutter/material.dart'; | |
| import 'package:flutter_bloc/flutter_bloc.dart'; | |
| import 'package:go_router/go_router.dart'; | |
| import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; | |
| import 'package:hadi_hoda_flutter/core/params/question_params.dart'; | |
| import 'package:hadi_hoda_flutter/core/routers/hero_dialog_route.dart'; | |
| import 'package:hadi_hoda_flutter/core/routers/my_routes.dart'; | |
| import 'package:hadi_hoda_flutter/core/services/audio_service.dart'; | |
| import 'package:hadi_hoda_flutter/core/status/base_status.dart'; | |
| import 'package:hadi_hoda_flutter/core/utils/context_provider.dart'; | |
| import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; | |
| import 'package:hadi_hoda_flutter/core/widgets/dialog/hadith_dialog.dart'; | |
| import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; | |
| import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart'; | |
| import 'package:hadi_hoda_flutter/features/question/domain/entity/question_entity.dart'; | |
| import 'package:hadi_hoda_flutter/features/question/domain/usecases/get_level_usecase.dart'; | |
| import 'package:hadi_hoda_flutter/features/question/domain/usecases/get_next_level_usecase.dart'; | |
| import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_event.dart'; | |
| import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_state.dart'; | |
| import 'package:hadi_hoda_flutter/features/question/presentation/ui/screens/answer_screen.dart'; | |
| import 'package:showcaseview/showcaseview.dart'; | |
| 
 | |
| class QuestionBloc extends Bloc<QuestionEvent, QuestionState> { | |
|   /// ------------constructor------------ | |
|   QuestionBloc( | |
|       this._getLevelUseCase, | |
|       this._getNextLevelUseCase, | |
|       this._audioService, | |
|       ) : super(QuestionState()) { | |
|     volumeStream = _audioService.volumeStream(); | |
|     on<GetLevelEvent>(_getLevelEvent); | |
|     on<ChooseAnswerEvent>(_chooseAnswerEvent); | |
|     on<GetNextLevelEvent>(_getNextLevelEvent); | |
|   } | |
| 
 | |
|   @override | |
|   Future<void> close() { | |
|     confettiController.dispose(); | |
|     return super.close(); | |
|   } | |
| 
 | |
|   /// ------------UseCases------------ | |
|   final GetLevelUseCase _getLevelUseCase; | |
|   final GetNextLevelUseCase _getNextLevelUseCase; | |
| 
 | |
|   /// ------------Variables------------ | |
|   final List<GlobalKey> keys = [ | |
|     GlobalKey(), | |
|     GlobalKey(), | |
|     GlobalKey(), | |
|     GlobalKey(), | |
|   ]; | |
|   late final Stream<double> volumeStream; | |
|   bool isPlaying = false; | |
| 
 | |
|   /// ------------Controllers------------ | |
|   final AudioService _audioService; | |
|   final ConfettiController confettiController = ConfettiController( | |
|     duration: Duration(seconds: 1), | |
|   ); | |
| 
 | |
|   /// ------------Functions------------ | |
|   void startShowCase({required BuildContext context}) { | |
|     ShowCaseWidget.of(context).startShowCase([keys[1]]); | |
|   } | |
| 
 | |
|   void showHadith({required BuildContext context}) { | |
|     if(isPlaying) return; | |
|     showHadithDialog( | |
|       context: context, | |
|       hadith: state.levelEntity?.hadith ?? [], | |
|     ); | |
|   } | |
| 
 | |
|   void goToLevelPage({required BuildContext context}) { | |
|     context.pushReplacement(Routes.levelPage); | |
|   } | |
| 
 | |
|   Future<void> playVoice() async { | |
|     await _audioService.setAudio(filePath: state.currentQuestion?.audio); | |
|     await _audioService.play(); | |
|   } | |
| 
 | |
|   Future<void> changeMute() async { | |
|     await _audioService.changeMute(); | |
|   } | |
| 
 | |
|   Future<void> showAnswerDialog({ | |
|     required BuildContext context, | |
|     required AnswerEntity answerEntity, | |
|     bool? correct, | |
|   }) async { | |
|     await Navigator.of(context).push( | |
|       HeroDialogRoute( | |
|         builder: (dialogContext) { | |
|           return AnswerScreen(answerEntity: answerEntity, correct: correct); | |
|         }, | |
|       ), | |
|     ); | |
|   } | |
| 
 | |
|   Future<void> playback(BuildContext context) async { | |
|     if (isPlaying) return; | |
|     for (int i = 0; i < 4; i++) { | |
|       await Future.delayed(Duration(seconds: 1)); | |
|       if (context.mounted) { | |
|         await showAnswerDialog( | |
|           context: context, | |
|           answerEntity: state.currentQuestion?.answers?[i] ?? AnswerEntity(), | |
|         ); | |
|         isPlaying = true; | |
|       } | |
|     } | |
|     isPlaying = false; | |
|   } | |
| 
 | |
|   /// ------------Event Calls------------ | |
|   FutureOr<void> _getLevelEvent(GetLevelEvent event, Emitter<QuestionState> emit) async { | |
|     await _getLevelUseCase(QuestionParams(id: int.parse(event.id ?? '0'))).then( | |
|       (value) { | |
|         value.fold( | |
|           (data) async { | |
|             final LevelEntity level = LevelEntity( | |
|               id: data.id, | |
|               order: data.order, | |
|               title: data.title, | |
|               hadith: data.hadith, | |
|               questions: [ | |
|                 ...?data.questions, | |
|                 QuestionEntity(order: (data.questions?.length ?? 0) + 1) | |
|               ], | |
|             ); | |
|             emit(state.copyWith( | |
|               getQuestionStatus: BaseComplete(''), | |
|               levelEntity: level, | |
|               currentQuestion: data.questions?.first, | |
|             )); | |
|            await playVoice(); | |
|            if(event.context.mounted){ | |
|              playback(event.context); | |
|            } | |
|           }, | |
|           (error) { | |
|             emit(state.copyWith(getQuestionStatus: BaseError(error.errorMessage))); | |
|           }, | |
|         ); | |
|       }, | |
|     ); | |
|   } | |
| 
 | |
|   FutureOr<void> _chooseAnswerEvent(ChooseAnswerEvent event, | |
|       Emitter<QuestionState> emit,) async { | |
|     emit(state.copyWith(correctAnswer: event.chooseCorrectAnswer)); | |
| 
 | |
|     if (event.chooseCorrectAnswer) { | |
|       confettiController.play(); | |
|       await showAnswerDialog( | |
|           answerEntity: state.currentQuestion?.answers?.singleWhere((e) => | |
|           e.order == event.correctAnswer) ?? AnswerEntity(), | |
|           context: ContextProvider.context, | |
|         correct: true, | |
|       ); | |
|       await Future.delayed(Duration(seconds: 2), () async { | |
|         final QuestionEntity? findPreQuestion = state.currentQuestion; | |
|         final int findIndex = (findPreQuestion?.order ?? 1); | |
|         emit( | |
|           state.copyWith( | |
|             currentQuestion: state.levelEntity?.questions?[findIndex], | |
|           ), | |
|         ); | |
|         if (state.currentQuestion?.order == | |
|             state.levelEntity?.questions?.length) { | |
|           int currentLevel = int.parse( | |
|               LocalStorage.readData(key: MyConstants.currentLevel) ?? '1'); | |
|           if (state.levelEntity?.order == currentLevel) { | |
|             ++currentLevel; | |
|             await LocalStorage.saveData( | |
|               key: MyConstants.currentLevel, | |
|               value: '$currentLevel', | |
|             ); | |
|           } | |
|         } else { | |
|           await playVoice(); | |
|           if(event.context.mounted){ | |
|             playback(event.context); | |
|           } | |
|         } | |
|       }); | |
|     } | |
|   } | |
| 
 | |
|   FutureOr<void> _getNextLevelEvent(GetNextLevelEvent event, | |
|       Emitter<QuestionState> emit) async { | |
|     await _getNextLevelUseCase(QuestionParams()).then((value) => | |
|         value.fold( | |
|           (data) { | |
|             ContextProvider.context.pushReplacementNamed( | |
|               Routes.questionPage, | |
|               pathParameters: { | |
|                 'id': '${data.id}' | |
|               }, | |
|             ); | |
|           }, | |
|           (error) { | |
|             goToLevelPage(context: ContextProvider.context); | |
|           }, | |
|         ), | |
|     ); | |
|   } | |
| }
 |