diff --git a/assets/images/unmusic.svg b/assets/images/unmusic.svg new file mode 100644 index 0000000..fca9700 --- /dev/null +++ b/assets/images/unmusic.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/common_ui/resources/my_assets.dart b/lib/common_ui/resources/my_assets.dart index 33e746f..23d84bb 100644 --- a/lib/common_ui/resources/my_assets.dart +++ b/lib/common_ui/resources/my_assets.dart @@ -45,4 +45,5 @@ class MyAssets { static const String diamondBig = 'assets/images/diamond_big.png'; static const String ship = 'assets/images/ship.png'; static const String shiny = 'assets/images/shiny.png'; + static const String unMusic = 'assets/images/unmusic.svg'; } \ No newline at end of file diff --git a/lib/core/routers/my_routes.dart b/lib/core/routers/my_routes.dart index a4cf4a9..0f9ef04 100644 --- a/lib/core/routers/my_routes.dart +++ b/lib/core/routers/my_routes.dart @@ -63,7 +63,7 @@ GoRouter get appPages => GoRouter( name: Routes.homePage, path: Routes.homePage, builder: (context, state) => BlocProvider( - create: (context) => HomeBloc(), + create: (context) => HomeBloc(locator()), child: const HomePage(), ), ), @@ -71,7 +71,7 @@ GoRouter get appPages => GoRouter( name: Routes.levelPage, path: Routes.levelPage, builder: (context, state) => BlocProvider( - create: (context) => LevelBloc(locator())..add(SetCurrentLevelEvent()), + create: (context) => LevelBloc(locator(), locator())..add(SetCurrentLevelEvent()), child: const LevelPage(), ), ), @@ -80,7 +80,7 @@ GoRouter get appPages => GoRouter( path: '${Routes.questionPage}/:id', builder: (context, state) => BlocProvider( create: (context) => - QuestionBloc(locator(), locator()) + QuestionBloc(locator(), locator(), locator()) ..add(GetLevelEvent(state.pathParameters['id'])), child: const QuestionPage(), ), diff --git a/lib/core/services/audio_service.dart b/lib/core/services/audio_service.dart new file mode 100644 index 0000000..9670981 --- /dev/null +++ b/lib/core/services/audio_service.dart @@ -0,0 +1,81 @@ +import 'package:flutter/foundation.dart'; +import 'package:just_audio/just_audio.dart'; + +class AudioService { + final AudioPlayer _player = AudioPlayer(); + + Future setAudio({String? filePath}) async { + try { + return _player.setAudioSource(AudioSource.file(filePath ?? '')); + } catch (e) { + if (kDebugMode) { + print('$e'); + } + return Duration.zero; + } + } + + Future play() async { + try { + return _player.play(); + } catch (e) { + if (kDebugMode) { + print('$e'); + } + } + } + + Future pause() async { + try { + return _player.pause(); + } catch (e) { + if (kDebugMode) { + print('$e'); + } + } + } + + Future stop() async { + try { + return _player.stop(); + } catch (e) { + if (kDebugMode) { + print('$e'); + } + } + } + + Future dispose() async { + try { + return _player.dispose(); + } catch (e) { + if (kDebugMode) { + print('$e'); + } + } + } + + Future changeMute() async { + try { + if (_player.volume == 0) { + await _player.setVolume(1); + } else { + await _player.setVolume(0); + } + } catch (e) { + if (kDebugMode) { + print('$e'); + } + } + } + + Stream volumeStream() async* { + try { + yield* _player.volumeStream; + } catch (e) { + if (kDebugMode) { + print('$e'); + } + } + } +} diff --git a/lib/features/home/presentation/bloc/home_bloc.dart b/lib/features/home/presentation/bloc/home_bloc.dart index c9c59a8..1ba9839 100644 --- a/lib/features/home/presentation/bloc/home_bloc.dart +++ b/lib/features/home/presentation/bloc/home_bloc.dart @@ -4,21 +4,27 @@ import 'package:bloc/bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:go_router/go_router.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/widgets/about_us_dialog/about_us_dialog.dart'; import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_event.dart'; import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_state.dart'; class HomeBloc extends Bloc { /// ------------constructor------------ - HomeBloc() : super(const HomeState()) { + HomeBloc( + this._audioService, + ) : super(const HomeState()) { + volumeStream = _audioService.volumeStream(); on(_getHomeEvent); } /// ------------UseCases------------ /// ------------Variables------------ + late final Stream volumeStream; /// ------------Controllers------------ + final AudioService _audioService; /// ------------Functions------------ void goToLevelPage(BuildContext context){ @@ -33,6 +39,9 @@ class HomeBloc extends Bloc { showAboutUsDialog(context: context); } + Future changeMute() async { + await _audioService.changeMute(); + } /// ------------Api Calls------------ FutureOr _getHomeEvent(event, emit) async {} } diff --git a/lib/features/home/presentation/ui/home_page.dart b/lib/features/home/presentation/ui/home_page.dart index 3234e84..2905c4f 100644 --- a/lib/features/home/presentation/ui/home_page.dart +++ b/lib/features/home/presentation/ui/home_page.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; -import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; -import 'package:hadi_hoda_flutter/core/utils/check_platform.dart'; +import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; @@ -16,7 +15,6 @@ class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(backgroundColor: MyColors.transparent), extendBodyBehindAppBar: true, body: DecoratedBox( decoration: BoxDecoration( @@ -41,13 +39,19 @@ class HomePage extends StatelessWidget { ); } - Positioned _music(BuildContext context) { - return Positioned( - top: checkSize(context: context, mobile: 36, tablet: 60), - right: checkSize(context: context, mobile: 16, tablet: 30), - child: MyImage( - image: MyAssets.musicOn, - size: checkSize(context: context, tablet: 120), + Widget _music(BuildContext context) { + return PositionedDirectional( + top: MediaQuery.viewPaddingOf(context).top + MySpaces.s16, + end: MySpaces.s16, + child: StreamBuilder( + initialData: 1, + stream: context.read().volumeStream, + builder: (context, snapshot) => InkWell( + onTap: () => context.read().changeMute(), + child: MyImage( + image: snapshot.data == 1 ? MyAssets.musicOn : MyAssets.musicOff, + ), + ), ), ); } @@ -58,7 +62,7 @@ class HomePage extends StatelessWidget { top: 146, child: MyImage( image: MyAssets.hadiHoda, - size: checkSize(context: context, mobile: 232, tablet: 400), + size: 232, fit: BoxFit.cover, ), ); @@ -66,9 +70,9 @@ class HomePage extends StatelessWidget { Positioned _bottomBtns(BuildContext context) { return Positioned( - bottom: checkSize(context: context, mobile: 40, tablet: 60), - left: checkSize(context: context, mobile: 16, tablet: 30), - right: checkSize(context: context, mobile: 16, tablet: 30), + bottom: MediaQuery.viewPaddingOf(context).bottom + MySpaces.s16, + left: MySpaces.s16, + right: MySpaces.s16, child: Row( crossAxisAlignment: CrossAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -77,7 +81,6 @@ class HomePage extends StatelessWidget { onTap: () => context.read().goToLanguagePage(context), child: MyImage( image: MyAssets.language, - size: checkSize(context: context, tablet: 120), ), ), MyButton( @@ -89,7 +92,6 @@ class HomePage extends StatelessWidget { onTap: () => context.read().showAboutUs(context), child: MyImage( image: MyAssets.theme, - size: checkSize(context: context, tablet: 120), ), ), ], diff --git a/lib/features/level/presentation/bloc/level_bloc.dart b/lib/features/level/presentation/bloc/level_bloc.dart index 36e8d40..23b2d8c 100644 --- a/lib/features/level/presentation/bloc/level_bloc.dart +++ b/lib/features/level/presentation/bloc/level_bloc.dart @@ -6,6 +6,7 @@ import 'package:go_router/go_router.dart'; import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/params/level_params.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/local_storage.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; @@ -19,7 +20,9 @@ class LevelBloc extends Bloc { /// ------------constructor------------ LevelBloc( this._getLeveslUseCase, + this._audioService, ) : super(const LevelState()) { + volumeStream = _audioService.volumeStream(); on(_getLevelListEvent); on(_setCurrentLevelEvent); on(_startScrollEvent); @@ -64,9 +67,13 @@ class LevelBloc extends Bloc { final List bottom8LevelList = []; final List top12LevelList = []; + late final Stream volumeStream; + + /// ------------Controllers------------ final ScrollController scrollController = ScrollController(); + final AudioService _audioService; /// ------------Functions------------ void goToQuestionPage(BuildContext context, LevelEntity level){ @@ -96,6 +103,10 @@ class LevelBloc extends Bloc { } } + Future changeMute() async { + await _audioService.changeMute(); + } + /// ------------Api Calls------------ FutureOr _getLevelListEvent(GetLevelListEvent event, Emitter emit) async { diff --git a/lib/features/level/presentation/ui/level_page.dart b/lib/features/level/presentation/ui/level_page.dart index fd6c9ab..08d8fff 100644 --- a/lib/features/level/presentation/ui/level_page.dart +++ b/lib/features/level/presentation/ui/level_page.dart @@ -67,7 +67,7 @@ class LevelPage extends StatelessWidget { return Positioned( left: MySpaces.s16, right: MySpaces.s16, - top: MediaQuery.viewPaddingOf(context).top, + top: MediaQuery.viewPaddingOf(context).top + MySpaces.s16, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -77,8 +77,15 @@ class LevelPage extends StatelessWidget { image: MyAssets.homeButton, ), ), - MyImage( - image: MyAssets.musicOn, + StreamBuilder( + initialData: 1, + stream: context.read().volumeStream, + builder: (context, snapshot) => InkWell( + onTap: () => context.read().changeMute(), + child: MyImage( + image: snapshot.data == 1 ? MyAssets.musicOn : MyAssets.musicOff, + ), + ), ), ], ), diff --git a/lib/features/question/presentation/bloc/question_bloc.dart b/lib/features/question/presentation/bloc/question_bloc.dart index 5c0a87f..e33c3bb 100644 --- a/lib/features/question/presentation/bloc/question_bloc.dart +++ b/lib/features/question/presentation/bloc/question_bloc.dart @@ -7,6 +7,7 @@ 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/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'; @@ -24,7 +25,9 @@ class QuestionBloc extends Bloc { QuestionBloc( this._getLevelUseCase, this._getNextLevelUseCase, + this._audioService, ) : super(QuestionState()) { + volumeStream = _audioService.volumeStream(); on(_getLevelEvent); on(_chooseAnswerEvent); on(_getNextLevelEvent); @@ -47,9 +50,10 @@ class QuestionBloc extends Bloc { GlobalKey(), GlobalKey(), ]; - + late final Stream volumeStream; /// ------------Controllers------------ + final AudioService _audioService; final ConfettiController confettiController = ConfettiController( duration: Duration(seconds: 1), ); @@ -67,6 +71,15 @@ class QuestionBloc extends Bloc { context.pushReplacement(Routes.levelPage); } + Future playVoice() async { + await _audioService.setAudio(filePath: state.currentQuestion?.audio); + await _audioService.play(); + } + + Future changeMute() async { + await _audioService.changeMute(); + } + /// ------------Event Calls------------ FutureOr _getLevelEvent(GetLevelEvent event, Emitter emit) async { await _getLevelUseCase(QuestionParams(id: int.parse(event.id ?? '0'))).then( @@ -87,6 +100,7 @@ class QuestionBloc extends Bloc { levelEntity: level, currentQuestion: data.questions?.first, )); + playVoice(); }, (error) { emit(state.copyWith(getQuestionStatus: BaseError(error.errorMessage))); @@ -119,6 +133,8 @@ class QuestionBloc extends Bloc { key: MyConstants.currentLevel, value: '$currentLevel', ); + } else { + await playVoice(); } }); } diff --git a/lib/features/question/presentation/ui/question_page.dart b/lib/features/question/presentation/ui/question_page.dart index 9a7912b..70efad9 100644 --- a/lib/features/question/presentation/ui/question_page.dart +++ b/lib/features/question/presentation/ui/question_page.dart @@ -105,7 +105,14 @@ class QuestionPage extends StatelessWidget { onTap: () => context.read().showHadith(context: context), ), MySpaces.s10.gapWidth, - GlassyButton(image: MyAssets.music, onTap: () {}), + StreamBuilder( + initialData: 1, + stream: context.read().volumeStream, + builder: (context, snapshot) => GlassyButton( + image: snapshot.data == 1 ? MyAssets.music : MyAssets.unMusic, + onTap: () => context.read().changeMute(), + ), + ), ], ); } diff --git a/lib/features/question/presentation/ui/widgets/glassy_button.dart b/lib/features/question/presentation/ui/widgets/glassy_button.dart index e346f4d..b3beb7b 100644 --- a/lib/features/question/presentation/ui/widgets/glassy_button.dart +++ b/lib/features/question/presentation/ui/widgets/glassy_button.dart @@ -32,7 +32,7 @@ class GlassyButton extends StatelessWidget { borderRadius: BorderRadius.all(Radius.circular(100)), child: Padding( padding: const EdgeInsets.all(MySpaces.s12), - child: MyImage(image: image), + child: MyImage(image: image, size: MySpaces.s22), ), ), ), diff --git a/lib/init_bindings.dart b/lib/init_bindings.dart index 84d2bfb..4741241 100644 --- a/lib/init_bindings.dart +++ b/lib/init_bindings.dart @@ -4,6 +4,7 @@ import 'package:get_it/get_it.dart'; import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/network/http_request.dart'; import 'package:hadi_hoda_flutter/core/network/http_request_impl.dart'; +import 'package:hadi_hoda_flutter/core/services/audio_service.dart'; import 'package:hadi_hoda_flutter/features/download/data/datasource/download_datasource.dart'; import 'package:hadi_hoda_flutter/features/download/data/repository_impl/download_repository_impl.dart'; import 'package:hadi_hoda_flutter/features/download/domain/repository/download_repository.dart'; @@ -41,6 +42,7 @@ final GetIt locator = GetIt.I; void initBindings() { /// Classes locator.registerSingleton(HttpRequestImpl()); + locator.registerSingleton(AudioService()); /// Sample Feature locator.registerLazySingleton(() => SampleDatasourceImpl(locator())); diff --git a/pubspec.lock b/pubspec.lock index b77f400..1b61d45 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -41,6 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.13.0" + audio_session: + dependency: transitive + description: + name: audio_session + sha256: "8f96a7fecbb718cb093070f868b4cdcb8a9b1053dce342ff8ab2fde10eb9afb7" + url: "https://pub.dev" + source: hosted + version: "0.2.2" bloc: dependency: "direct main" description: @@ -421,6 +429,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.9.0" + just_audio: + dependency: "direct main" + description: + name: just_audio + sha256: "9694e4734f515f2a052493d1d7e0d6de219ee0427c7c29492e246ff32a219908" + url: "https://pub.dev" + source: hosted + version: "0.10.5" + just_audio_platform_interface: + dependency: transitive + description: + name: just_audio_platform_interface + sha256: "2532c8d6702528824445921c5ff10548b518b13f808c2e34c2fd54793b999a6a" + url: "https://pub.dev" + source: hosted + version: "4.6.0" + just_audio_web: + dependency: transitive + description: + name: just_audio_web + sha256: "6ba8a2a7e87d57d32f0f7b42856ade3d6a9fbe0f1a11fabae0a4f00bb73f0663" + url: "https://pub.dev" + source: hosted + version: "0.4.16" leak_tracker: dependency: transitive description: @@ -661,6 +693,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.0" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + url: "https://pub.dev" + source: hosted + version: "0.28.0" shared_preferences: dependency: "direct main" description: @@ -770,6 +810,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" stack_trace: dependency: transitive description: @@ -802,6 +850,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + url: "https://pub.dev" + source: hosted + version: "3.4.0" term_glyph: dependency: transitive description: @@ -834,6 +890,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" vector_graphics: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8f6a09e..84f89c0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,6 +23,7 @@ dependencies: go_router: ^16.1.0 hive: ^2.2.3 intl: ^0.20.2 + just_audio: ^0.10.5 path_drawing: ^1.0.1 path_provider: ^2.1.5 pretty_dio_logger: ^1.4.0