Browse Source

update

develop
sina 5 days ago
parent
commit
133e95f4e9
  1. 6
      android/app/src/main/AndroidManifest.xml
  2. BIN
      assets/audios/incorrect_answers/ar_1.mp3
  3. BIN
      assets/audios/incorrect_answers/ar_2.mp3
  4. BIN
      assets/audios/incorrect_answers/ar_3.mp3
  5. BIN
      assets/audios/incorrect_answers/ar_4.mp3
  6. BIN
      assets/audios/incorrect_answers/ar_5.mp3
  7. BIN
      assets/audios/incorrect_answers/en_1.mp3
  8. BIN
      assets/audios/incorrect_answers/en_2.mp3
  9. BIN
      assets/audios/incorrect_answers/en_3.mp3
  10. BIN
      assets/audios/incorrect_answers/en_4.mp3
  11. BIN
      assets/audios/incorrect_answers/fa_1.mp3
  12. BIN
      assets/audios/incorrect_answers/fa_2.mp3
  13. BIN
      assets/audios/incorrect_answers/fa_3.mp3
  14. BIN
      assets/audios/incorrect_answers/fa_4.mp3
  15. BIN
      assets/audios/incorrect_answers/fa_5.mp3
  16. BIN
      assets/audios/incorrect_answers/fa_6.mp3
  17. BIN
      assets/audios/incorrect_answers/incorrect_answer.mp3
  18. BIN
      assets/fonts/NotoSansArabic.ttf
  19. 26
      assets/svg/button_3.5.svg
  20. 219
      assets/svg/lock.svg
  21. 2
      lib/common_ui/resources/my_assets.dart
  22. 43
      lib/common_ui/resources/my_audios.dart
  23. 135
      lib/common_ui/resources/my_text_style.dart
  24. 10
      lib/core/middlewares/my_middlewares.dart
  25. 3
      lib/core/network/interceptors/logging_interceptor.dart
  26. 5
      lib/core/params/level_params.dart
  27. 3
      lib/core/routers/my_routes.dart
  28. 7
      lib/core/services/audio_service.dart
  29. 3
      lib/core/utils/app_life_cycle.dart
  30. 18
      lib/core/utils/my_device.dart
  31. 16
      lib/core/utils/screen_size.dart
  32. 4
      lib/core/widgets/animations/fade_anim.dart
  33. 4
      lib/core/widgets/animations/fade_anim_delayed.dart
  34. 8
      lib/core/widgets/animations/slide_anim.dart
  35. 4
      lib/core/widgets/animations/slide_down_fade.dart
  36. 4
      lib/core/widgets/animations/slide_up_fade.dart
  37. 4
      lib/core/widgets/answer_box/answer_box.dart
  38. 19
      lib/core/widgets/answer_box/answer_box_show.dart
  39. 11
      lib/core/widgets/answer_box/styles/text_box.dart
  40. 13
      lib/core/widgets/button/my_blue_button.dart
  41. 4
      lib/core/widgets/button/my_white_button.dart
  42. 50
      lib/core/widgets/dialog/hadith_dialog.dart
  43. 25
      lib/core/widgets/dialog/reward_dialog.dart
  44. 11
      lib/core/widgets/error/error_state.dart
  45. 28
      lib/core/widgets/page_transition/my_page_transition.dart
  46. 42
      lib/core/widgets/video/my_video_player.dart
  47. 14
      lib/features/download/presentation/ui/download_page.dart
  48. 13
      lib/features/guider/presentation/ui/guider_page.dart
  49. 107
      lib/features/home/presentation/ui/home_page.dart
  50. 75
      lib/features/home/presentation/ui/widgets/payment_dialog.dart
  51. 8
      lib/features/language/data/repository_impl/language_repository_impl.dart
  52. 137
      lib/features/language/presentation/ui/language_page.dart
  53. 58
      lib/features/level/data/datasource/level_datasource.dart
  54. 5
      lib/features/level/domain/entity/node_entity.dart
  55. 5
      lib/features/level/domain/entity/node_entity.g.dart
  56. 52
      lib/features/level/presentation/bloc/level_bloc.dart
  57. 37
      lib/features/level/presentation/ui/level_page.dart
  58. 41
      lib/features/level/presentation/ui/widgets/coming_soon_level.dart
  59. 2
      lib/features/level/presentation/ui/widgets/level_path.dart
  60. 44
      lib/features/level/presentation/ui/widgets/node_widget.dart
  61. 16
      lib/features/level/presentation/ui/widgets/play_button.dart
  62. 2
      lib/features/question/data/model/hadith_model.dart
  63. 7
      lib/features/question/domain/entity/answer_entity.g.dart
  64. 3
      lib/features/question/domain/entity/hadith_entity.dart
  65. 2
      lib/features/question/domain/entity/question_entity.dart
  66. 7
      lib/features/question/domain/entity/question_entity.g.dart
  67. 83
      lib/features/question/presentation/bloc/question_bloc.dart
  68. 26
      lib/features/question/presentation/ui/question_page.dart
  69. 1
      lib/features/question/presentation/ui/screens/answer_screen.dart
  70. 14
      lib/features/question/presentation/ui/screens/diamond_screen.dart
  71. 56
      lib/features/question/presentation/ui/screens/question_screen.dart
  72. 2
      lib/features/splash/presentation/bloc/splash_bloc.dart
  73. 23
      lib/features/splash/presentation/ui/splash_page.dart
  74. 107
      lib/init_bindings.dart
  75. 3
      lib/l10n/app_ar.arb
  76. 3
      lib/l10n/app_de.arb
  77. 4
      lib/l10n/app_en.arb
  78. 3
      lib/l10n/app_fr.arb
  79. 12
      lib/l10n/app_localizations.dart
  80. 6
      lib/l10n/app_localizations_ar.dart
  81. 6
      lib/l10n/app_localizations_de.dart
  82. 6
      lib/l10n/app_localizations_en.dart
  83. 6
      lib/l10n/app_localizations_fr.dart
  84. 6
      lib/l10n/app_localizations_ru.dart
  85. 6
      lib/l10n/app_localizations_tr.dart
  86. 3
      lib/l10n/app_ru.arb
  87. 3
      lib/l10n/app_tr.arb
  88. 7
      pubspec.yaml

6
android/app/src/main/AndroidManifest.xml

@ -44,4 +44,10 @@
<data android:mimeType="text/plain"/> <data android:mimeType="text/plain"/>
</intent> </intent>
</queries> </queries>
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
</queries>
</manifest> </manifest>

BIN
assets/audios/incorrect_answers/ar_1.mp3

BIN
assets/audios/incorrect_answers/ar_2.mp3

BIN
assets/audios/incorrect_answers/ar_3.mp3

BIN
assets/audios/incorrect_answers/ar_4.mp3

BIN
assets/audios/incorrect_answers/ar_5.mp3

BIN
assets/audios/incorrect_answers/en_1.mp3

BIN
assets/audios/incorrect_answers/en_2.mp3

BIN
assets/audios/incorrect_answers/en_3.mp3

BIN
assets/audios/incorrect_answers/en_4.mp3

BIN
assets/audios/incorrect_answers/fa_1.mp3

BIN
assets/audios/incorrect_answers/fa_2.mp3

BIN
assets/audios/incorrect_answers/fa_3.mp3

BIN
assets/audios/incorrect_answers/fa_4.mp3

BIN
assets/audios/incorrect_answers/fa_5.mp3

BIN
assets/audios/incorrect_answers/fa_6.mp3

BIN
assets/audios/incorrect_answers/incorrect_answer.mp3

BIN
assets/fonts/NotoSansArabic.ttf

26
assets/svg/button_3.5.svg

@ -0,0 +1,26 @@
<svg width="167" height="93" viewBox="0 0 167 93" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_138_2505)">
<path d="M158.295 75.4722C158.141 75.6483 157.976 75.812 157.8 75.9622C143.895 88.3956 23.2313 87.9564 6.39091 76.7899C5.65757 76.3688 5.04727 75.7307 4.62962 74.9486C-1.3186 61.4339 1.91044 20.586 11.3658 11.7508C21.5319 2.29052 142.876 -2.00037 155.421 10.0614C164.675 18.8628 169.063 63.1064 158.295 75.4722Z" fill="url(#paint0_linear_138_2505)"/>
</g>
<path d="M4.67588 72.2628C10.4233 85.3551 146.444 86.4025 158.295 72.8034C169.11 60.4375 164.675 16.1771 155.467 7.35883C142.922 -4.66919 21.5781 -0.395189 11.4275 9.04816C1.95668 17.8665 -1.27235 58.7144 4.67588 72.2628Z" fill="url(#paint1_linear_138_2505)"/>
<defs>
<filter id="filter0_d_138_2505" x="-0.00141752" y="2.68311" width="166.237" height="90.2875" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset dy="6.11371"/>
<feGaussianBlur stdDeviation="0.815162"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_138_2505"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_138_2505" result="shape"/>
</filter>
<linearGradient id="paint0_linear_138_2505" x1="83.1462" y1="85.2366" x2="83.1462" y2="2.67907" gradientUnits="userSpaceOnUse">
<stop stop-color="#8B9AD1"/>
<stop offset="1" stop-color="#CEE0FF"/>
</linearGradient>
<linearGradient id="paint1_linear_138_2505" x1="83.1461" y1="82.5509" x2="83.1461" y2="-0.00664309" gradientUnits="userSpaceOnUse">
<stop stop-color="#CADCFF"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
</defs>
</svg>

219
assets/svg/lock.svg

@ -0,0 +1,219 @@
<svg width="79" height="92" viewBox="0 0 79 92" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1_21189)">
<path d="M68.8871 37.8113C68.8871 37.8113 62.2779 37.7755 57.504 37.9663C52.1068 38.8674 46.8138 40.309 41.705 42.2693C41.705 42.2693 41.0258 47.7374 35.7553 49.4767L34.1695 45.5591C34.1695 45.5591 29.3646 45.7743 23.5978 46.3813C23.5978 46.3813 14.3602 50.3415 9.93615 52.9584L14.1665 57.3415L61.2053 46.8176L68.8871 37.8113Z" fill="#8B6ED1"/>
<path d="M16.5965 51.9352C16.2293 52.0658 11.2738 52.6591 11.0024 52.5067C10.731 52.3542 20.8189 47.7855 22.9143 47.1222C24.7959 46.5215 30.516 45.9002 31.5872 45.9563C32.0762 45.9807 17.9202 51.4733 16.5965 51.9352Z" fill="#B6D3FF"/>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M25.0028 47.4523C26.1584 47.0033 28.5951 46.6935 22.8118 49.3797C18.7798 51.2589 14.3425 52.2656 13.8586 52.1003C13.1408 51.8424 21.8006 48.7071 25.0028 47.4523Z" fill="#912790"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M59.7212 38.0698C61.8301 37.8372 62.9853 37.8868 62.2849 38.2139C61.5844 38.541 49.6974 40.6967 49.5738 40.2152C49.4501 39.7337 57.097 38.3602 59.7212 38.0698Z" fill="#912790"/>
</g>
<path d="M19.2982 53.7255L10.9023 54.1846C10.9023 54.1846 21.6445 76.2677 24.9676 79.366C28.2908 82.4643 39.656 83.0431 40.7587 82.9567C43.8613 82.7312 52.8156 82.2351 52.8156 82.2351L43.7869 57.023L19.2982 53.7255Z" fill="url(#paint0_linear_1_21189)"/>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M31.5612 77.278C31.7622 78.0283 24.7814 78.8266 24.1502 78.1915C23.519 77.5565 14.6906 61.335 14.7349 60.46C14.7792 59.5849 21.1157 59.7635 21.5597 60.6203C22.0037 61.477 31.3457 76.494 31.5612 77.278Z" fill="#6E5066"/>
</g>
<path d="M19.2981 53.7251L32.8659 47.3179C36.4967 52.7248 43.6725 52.3943 43.6725 52.3943L45.1331 52.0029C45.1331 52.0029 51.4662 48.5475 51.7342 42.0048L67.0156 40.3119C67.7031 42.1972 69.3051 66.0432 67.9701 68.5239C66.6351 71.0046 55.1836 81.2087 53.0023 82.2114L52.8222 82.2596C50.4217 82.4847 35.3495 79.4358 33.0118 78.0143C30.674 76.5927 19.7014 55.6703 19.2981 53.7251Z" fill="url(#paint1_linear_1_21189)"/>
<path d="M64.6999 68.1995C63.6256 69.4524 54.0314 78.7246 51.3653 79.4389C50.0598 79.7887 35.9425 76.9022 33.8625 74.8219C31.7825 72.7415 26.0562 60.6746 26.324 60.3134C26.5917 59.9521 63.9523 49.743 64.7482 50.3125C65.5441 50.8819 65.3864 67.4205 64.6999 68.1995Z" fill="url(#paint2_linear_1_21189)"/>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M62.1069 66.5297C61.5228 68.9915 33.5702 71.2542 32.394 70.406C31.2178 69.5578 27.7736 63.3665 28.5412 62.0296C29.3088 60.6927 57.6065 48.0333 60.4502 50.2629C63.294 52.4924 62.5969 64.5167 62.1069 66.5297Z" fill="#6E5066"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M62.8623 68.7027C61.8853 69.8384 53.7003 78.1272 49.964 79.3695C50.438 79.418 50.9144 79.4389 51.3908 79.4322C54.0519 78.7192 63.6461 69.447 64.7203 68.1941C65.3869 67.4204 65.628 50.9132 64.7536 50.3111C64.1701 49.8939 43.6897 55.3333 32.8975 58.3537C43.9915 55.2792 62.3514 50.4294 62.9086 50.8484C63.7763 51.4254 63.5539 67.9223 62.8623 68.7027Z" fill="#6E5066"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M40.1705 82.9916C37.9683 80.5151 34.7739 78.4975 33.1846 78.9287C31.5953 79.3599 28.4821 80.0065 26.7172 79.702C24.9522 79.3976 17.7117 66.1013 17.6863 65.7864C17.6609 65.4715 24.3348 66.589 25.1781 66.615C25.4696 66.6227 28.8836 66.2387 28.8836 66.2387L29.4788 67.4195C29.4788 67.4195 34.2652 66.2549 34.5809 66.0524C35.1441 65.6334 35.251 60.19 44.5221 56.8748C50.6506 54.6966 57.8413 59.8841 57.8413 59.8841C57.8413 59.8841 61.5841 59.6264 63.4543 58.6428C63.7269 58.5001 65.1071 56.3879 65.3798 56.2451L68.2611 54.7332L68.0778 51.8284C68.0778 51.8284 53.9025 50.9463 44.6803 53.5836C35.4581 56.2209 15.4798 63.3539 15.4798 63.3539C15.4798 63.3539 22.4934 77.4241 25.1602 79.5938C27.8271 81.7635 30.5451 81.8233 33.9588 82.5384C36.0003 82.9628 38.089 83.1152 40.1705 82.9916Z" fill="#6E5066"/>
</g>
<path d="M59.8543 57.0335L57.0758 56.6683C57.0758 56.6683 53.6487 54.7827 48.5264 55.3939C40.5785 56.3441 36.0843 60.8401 36.0843 60.8401C36.0843 60.8401 38.7677 53.4678 48.5091 53.4685C58.3459 53.4651 59.8543 57.0335 59.8543 57.0335Z" fill="url(#paint3_linear_1_21189)"/>
<path d="M34.1985 45.5506C40.2394 51.829 44.1769 50.077 44.1769 50.077C44.1769 50.077 48.4673 49.5224 50.3448 40.983C50.3448 40.983 60.7916 38.4143 68.9061 37.8055L70.0521 52.767C70.0521 52.767 63.6647 55.9796 59.8512 57.0336C59.8512 57.0336 54.5584 52.8869 45.6726 55.359C36.7868 57.8311 34.5532 64.0213 34.5532 64.0213C30.8197 65.0538 23.8666 65.5551 23.8666 65.5551L17.1314 52.4237C24.1535 48.7783 34.1985 45.5506 34.1985 45.5506Z" fill="url(#paint4_linear_1_21189)"/>
<path d="M9.96875 52.9502L17.16 52.4172L23.8952 65.5487C23.8952 65.5487 17.6404 64.9944 14.1852 64.1242L9.96875 52.9502Z" fill="url(#paint5_linear_1_21189)"/>
<path d="M16.2766 51.168C16.2766 51.168 22.84 49.7524 25.7047 47.4784C24.8186 43.8244 24.3989 40.0729 24.4554 36.3134C24.6216 30.511 26.3677 24.2424 32.7063 22.3081C36.6731 21.1058 39.9257 22.4002 42.5213 24.7445C48.1031 29.7894 50.6331 39.6515 50.6331 39.6515C54.0809 39.934 60.2308 38.0556 60.2308 38.0556C60.1104 37.1063 59.9419 36.1637 59.726 35.2315C58.8098 31.0916 56.3381 23.1278 50.115 16.2496C40.2041 5.27179 28.9732 7.97551 28.9732 7.97551C28.9732 7.97551 17.9839 11.9012 14.6629 26.9981C14.2033 29.0916 13.8981 31.2162 13.7498 33.3544C13.0461 43.7935 16.2766 51.168 16.2766 51.168Z" fill="url(#paint6_linear_1_21189)"/>
<path d="M54.3117 74.2747L49.9712 65.8788C50.5937 65.2262 51.0366 64.4236 51.257 63.5491C51.4774 62.6746 51.4677 61.758 51.2288 60.8883C50.4214 58.215 47.7941 57.0533 44.4153 57.889L44.0401 57.9895C42.9974 58.2423 42.0112 58.6883 41.1329 59.3046C40.3513 59.8171 39.7475 60.5591 39.4045 61.4285C39.0616 62.298 38.9962 63.2523 39.2173 64.1604C39.446 65.085 39.9251 65.9285 40.6021 66.5984C41.2791 67.2683 42.1277 67.7384 43.0547 67.9572C43.0547 67.9572 44.5007 77.1555 44.7631 77.2943C45.3849 77.5941 54.3117 74.2747 54.3117 74.2747Z" fill="#513752"/>
<path d="M54.3149 74.2748C54.3149 74.2748 51.2904 65.6282 51.3156 65.482C51.3408 65.3359 53.1551 62.6034 52.5126 60.5453C51.6905 57.8974 49.0808 56.7417 45.744 57.5339L45.3689 57.6345C44.3385 57.8819 43.3652 58.3247 42.5017 58.9388C41.7373 59.4479 41.1493 60.181 40.8182 61.0377C40.4872 61.8943 40.4294 62.8324 40.6528 63.7232C40.8856 64.6412 41.3657 65.4777 42.0408 66.1419C42.716 66.806 43.5603 67.2723 44.482 67.49C44.482 67.49 46.2072 76.6295 46.3191 76.7068C46.4309 76.784 54.1615 74.5625 54.3149 74.2748Z" fill="url(#paint7_linear_1_21189)"/>
<path d="M42.0998 63.3459C41.2767 59.894 45.9367 58.9778 45.9367 58.9778C45.9367 58.9778 49.9978 58.5276 50.9311 61.0707C51.7805 63.4004 49.635 64.817 49.6689 64.9633C49.7319 65.1984 52.4256 72.7704 52.3926 73.1277C52.3597 73.485 47.4706 74.6663 47.3493 74.5541C47.1386 74.3478 45.8017 66.3373 45.7337 66.1036C45.6657 65.8698 42.7063 65.8693 42.0998 63.3459Z" fill="#513752"/>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M45.0559 59.3475C47.9157 58.4955 49.2672 60.6584 49.4854 62.0528C49.7035 63.4472 48.5363 65.3737 46.2541 65.76C43.9719 66.1464 42.3348 64.4781 42.1729 63.594C42.0111 62.7099 41.7287 60.3355 45.0559 59.3475Z" fill="#6E5066"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M46.2687 66.2054C46.8115 65.9903 48.0779 66.3747 48.5542 67.4319C49.0305 68.4891 51.0844 73.0533 50.8776 73.3821C50.6709 73.7109 47.4783 74.3412 47.4042 74.0448C47.3302 73.7483 45.4838 66.5176 46.2687 66.2054Z" fill="#6E5066"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M44.4933 67.497L43.0471 67.9221C43.0471 67.9221 40.0944 66.9065 39.5901 65.2242C39.0857 63.5419 39.9933 63.5078 40.4016 64.0311C40.8605 64.7584 41.3863 65.4414 41.9721 66.071C42.6255 66.8197 43.515 67.3229 44.4933 67.497Z" fill="#6E5066"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M54.3123 74.2748C52.85 75.4367 51.2311 76.3866 49.5036 77.0966C48.7733 77.2923 44.753 77.2544 44.753 77.2544L46.3278 76.7091L54.3123 74.2748Z" fill="#6E5066"/>
</g>
<g opacity="0.2" style="mix-blend-mode:multiply">
<path d="M52.5342 61.445C52.617 62.034 51.3572 61.294 51.1377 61.015C50.2534 59.8956 49.8073 58.6909 46.4547 58.844C44.4114 58.9412 42.4077 60.427 41.8877 61.5475C41.6684 62.0297 40.8712 62.3559 40.8197 61.9837C40.5827 60.1387 43.3829 57.5441 46.7907 57.537C50.1985 57.53 52.1144 58.5177 52.5342 61.445Z" fill="#7F6593"/>
</g>
<path d="M51.6485 58.9319C51.6485 58.9319 48.397 54.9406 41.5976 58.9766C41.5976 58.9766 47.1247 55.7747 51.6485 58.9319Z" fill="#EED6DD"/>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M50.0708 41.0572C50.1819 40.4913 43.1293 41.802 42.0833 42.4201C41.6206 42.6942 41.5654 45.6093 37.7245 48.5417C37.7245 48.5417 42.197 51.8681 45.6675 49.0136C48.0734 47.0011 49.6432 44.1645 50.0708 41.0572Z" fill="#912790"/>
</g>
<g opacity="0.3" style="mix-blend-mode:soft-light">
<path d="M23.5667 63.2876C24.1694 63.7963 36.2719 60.4837 37.3201 59.4738C39.875 57.0039 40.1186 53.7112 48.0165 52.174C55.7016 50.6831 58.9373 52.9148 59.873 53.2056C60.8087 53.4963 67.0741 51.1688 67.8907 50.3549C68.7073 49.541 68.5684 41.0594 68.4192 38.7619C68.3679 37.95 50.9879 41.0147 50.6751 42.1279C50.0257 44.4463 48.4648 50.2257 43.0262 50.777C38.7023 51.2064 35.4188 46.9556 34.2744 46.5063C33.8864 46.4682 33.4948 46.5181 33.1288 46.6525C33.2676 47.5106 33.2016 48.445 32.3794 48.7779C31.5572 49.1108 30.501 48.4503 29.7883 47.7513C25.5124 49.3205 19.6685 51.8407 18.8928 52.2469C17.7024 52.8661 22.964 62.779 23.5667 63.2876Z" fill="white"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M68.0038 39.731C69.1589 42.2414 68.6404 49.9503 67.891 50.3548C67.1416 50.7593 51.0503 42.6867 52.5241 41.8844C53.9979 41.082 67.5004 38.6328 68.0038 39.731Z" fill="#8F52BE"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M35.0735 60.6176C35.6337 60.4675 35.182 62.6633 33.6483 63.5622C32.1146 64.461 24.7637 65.6586 24.228 64.88C22.9947 63.0982 34.637 60.7292 35.0735 60.6176Z" fill="#BE81A9"/>
</g>
<g opacity="0.2" style="mix-blend-mode:screen">
<path d="M33.204 78.923C33.0709 79.7468 29.8917 80.3467 29.2082 80.3368C26.8807 80.2742 25.573 78.7751 26.0772 78.656C27.2471 78.3801 33.309 78.2944 33.204 78.923Z" fill="#FFB311"/>
</g>
<path d="M67.9649 68.5036C64.2845 72.6153 54.3512 81.142 52.7243 81.653C51.0974 82.164 37.7824 82.3918 33.428 81.9073C31.0846 81.6874 28.774 81.2003 26.5412 80.4555C26.5412 80.4555 31.4494 82.9467 40.1478 82.9964C48.8461 83.046 52.2375 82.4375 52.8228 82.2807C54.7531 81.6616 57.8773 78.8355 62.0857 74.9737C66.2942 71.1118 67.9649 68.5036 67.9649 68.5036Z" fill="#513752"/>
<path d="M25.1953 66.6094C28.165 70.1405 30.7828 73.9531 33.0116 77.9929C33.0116 77.9929 31.1841 76.4347 28.2903 71.6773C25.203 66.618 25.1953 66.6094 25.1953 66.6094Z" fill="#EED6DD"/>
<path d="M64.7018 68.1998C60.8966 72.5058 52.9927 78.8429 51.6124 79.3736C50.232 79.9043 35.6934 77.0663 34.1363 75.6768C32.5792 74.2873 31.5512 71.3514 31.5512 71.3514C31.5512 71.3514 31.8633 72.8762 35.0084 75.2501C38.1535 77.6241 49.6948 78.9599 51.4555 78.4881C54.249 77.7289 64.7018 68.1998 64.7018 68.1998Z" fill="#EED6DD"/>
<g opacity="0.3" style="mix-blend-mode:soft-light">
<path d="M16.8573 53.1687C17.8038 54.7003 23.1369 64.5995 22.6422 64.9143C22.1476 65.2291 16.4565 62.197 15.8915 61.169C14.9441 59.4338 12.7047 53.4772 13.134 53.2388C13.5634 53.0005 16.3443 52.3143 16.8573 53.1687Z" fill="white"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M23.7176 65.8959C24.7617 66.7313 26.3305 69.7849 26.1299 70.037C25.9294 70.2891 22.2628 75.2925 21.3741 74.1367C20.4854 72.9809 15.9748 64.9308 16.2886 64.8414C16.6024 64.7519 23.1727 65.4629 23.7176 65.8959Z" fill="#6E5066"/>
</g>
<path d="M26.7536 48.634C26.9172 49.0244 19.5279 54.6607 19.1189 54.6148C18.7099 54.5689 16.6325 52.7184 17.6159 52.1869C18.1522 51.8877 26.7536 48.634 26.7536 48.634Z" fill="#9F81FF"/>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M49.7071 41.1598C47.4946 40.3855 43.3271 42.1991 43.1199 43.0267C42.9128 43.8542 43.1475 49.6724 44.1614 49.4544C45.9375 49.0803 47.5862 47.5503 48.6553 45.6179C49.7245 43.6856 50.3225 41.3755 49.7071 41.1598Z" fill="#912790"/>
</g>
<path d="M23.0563 50.2789C23.8243 50.1643 20.9564 52.7662 20.1746 52.8899C19.3929 53.0136 18.1059 52.372 18.5234 52.0296C19.9821 51.3189 21.4985 50.7332 23.0563 50.2789Z" fill="white"/>
<path d="M68.8894 37.8108C67.1819 39.2012 51.6488 41.0151 50.3281 40.9883C50.3281 40.9883 58.7749 38.9984 63.2615 38.4556C67.7481 37.9129 68.8894 37.8108 68.8894 37.8108Z" fill="#9F81FF"/>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M59.7376 56.6196C58.4859 56.51 56.4793 54.4636 51.5636 54.3654C44.8836 54.2253 38.4537 59.0201 38.5581 58.5096C38.9093 56.7589 44.4773 51.1282 52.9812 52.093C58.5277 52.7245 60.0772 56.6465 59.7376 56.6196Z" fill="#BE81A9"/>
</g>
<path d="M57.9119 53.7474C57.9119 53.7474 55.6769 50.6685 50.4134 50.8726C47.2383 51.0294 44.2369 52.3692 42.0001 54.6281C42.0001 54.6281 50.2578 49.0915 57.9119 53.7474Z" fill="#9F81FF"/>
<path d="M19.819 57.6284C20.599 57.4783 23.2475 60.4196 23.3331 61.6995C23.4187 62.9793 23.0167 63.4998 22.6418 63.5413C22.2669 63.5828 20.6095 61.7591 20.0851 60.9024C19.5607 60.0458 19.1867 57.7496 19.819 57.6284Z" fill="#4F0086"/>
<path d="M22.8994 60.5769C23.3341 61.6989 23.2988 63.1076 22.6076 63.0891C21.8333 63.0607 20.8042 61.9211 20.4427 61.4121C20.4427 61.4121 21.8316 63.5544 22.8724 63.5971C23.9132 63.6399 23.5451 61.8461 23.3702 61.4533C23.2422 61.1465 23.0846 60.853 22.8994 60.5769Z" fill="#9F81FF"/>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M22.0044 59.4651C23.1172 61.097 21.7642 61.9099 21.0577 61.6542C20.3512 61.3985 19.776 60.4322 19.5847 59.438C19.3933 58.4438 19.4487 57.33 20.1964 57.7194C20.9153 58.1674 21.5315 58.7623 22.0044 59.4651Z" fill="#912774"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M32.9296 45.9713C33.1754 47.0688 33.532 48.6996 32.1692 48.8556C30.8064 49.0117 29.3822 47.2381 29.3822 47.2381C29.3822 47.2381 29.403 47.0556 30.8011 46.5309C31.48 46.2431 32.1969 46.0546 32.9296 45.9713Z" fill="#A34AA2"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M31.1504 46.5881C31.8644 46.6916 32.4119 48.3945 31.9228 48.6703C31.4338 48.9461 29.7818 47.9627 29.3859 47.2056C29.3799 47.2233 30.5145 46.4958 31.1504 46.5881Z" fill="#923B66"/>
</g>
<path d="M49.4772 50.5446C50.6709 50.4177 51.5201 51.7663 51.2269 51.7323C50.9336 51.6983 47.3112 52.4652 47.1212 52.2963C46.9312 52.1274 48.024 50.7034 49.4772 50.5446Z" fill="white"/>
<path d="M49.5184 57.4655C50.431 57.9501 47.0852 57.9888 46.7891 57.1836C46.7891 57.1836 48.6235 56.9869 49.5184 57.4655Z" fill="white"/>
<path d="M28.0627 70.7796C27.6624 69.6055 29.8991 70.5502 30.5986 71.9604C31.2982 73.3706 31.1494 75.3565 30.6617 75.0369C30.174 74.7172 28.3928 71.7312 28.0627 70.7796Z" fill="white"/>
<path d="M54.4583 27.4378C53.9581 27.5719 44.9805 12.7145 33.3201 15.4958C20.8576 18.4652 20.0288 34.4598 18.8589 34.9555C17.689 35.4513 14.8491 30.9154 15.4116 26.4919C16.5292 17.6575 25.3278 9.25784 31.2368 8.44118C37.1458 7.62452 45.1806 11.5404 49.6252 17.1635C54.0698 22.7866 54.8758 27.3152 54.4583 27.4378Z" fill="white"/>
<path d="M47.9277 15.7264C48.3669 16.4451 31.6408 12.8261 30.2841 12.2247C28.9274 11.6232 27.2028 9.16881 30.8934 8.49624C34.5839 7.82367 43.6128 8.70685 47.9277 15.7264Z" fill="white"/>
<path d="M21.6286 16.2164C22.7074 17.3212 19.4796 23.9225 18.3929 23.9885C17.3062 24.0545 16.926 22.5159 17.8892 20.108C18.5647 18.4473 21.2018 15.7839 21.6286 16.2164Z" fill="white"/>
<path d="M24.8245 12.9637C25.8196 13.7961 23.9647 14.7167 23.2607 14.6104C22.5568 14.5042 23.9654 12.2182 24.8245 12.9637Z" fill="white"/>
<g opacity="0.8" style="mix-blend-mode:multiply">
<path d="M65.3867 37.8105C67.2926 37.9807 68.2483 38.1857 68.4187 38.7619C68.5892 39.3381 69.5928 46.725 69.5928 46.725C69.5928 46.725 68.9622 37.9891 68.9049 37.7953C68.8476 37.6016 65.3867 37.8105 65.3867 37.8105Z" fill="#923B66"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M42.5302 24.7533C48.112 29.7983 50.6419 39.6604 50.6419 39.6604C54.0898 39.9428 60.2396 38.0644 60.2396 38.0644C60.1193 37.1151 59.9508 36.1726 59.7349 35.2403C57.6938 37.4867 55.7608 36.9753 54.4625 35.8114C53.1641 34.6474 50.298 28.4727 46.4036 25.2434C43.4179 22.7838 41.8279 23.4726 42.5302 24.7533Z" fill="#6E5066"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M15.8451 36.2679C16.7945 36.8498 23.1063 47.3606 22.4497 48.0512C21.793 48.7418 17.2595 51.0502 16.5461 49.7885C15.8328 48.5268 13.4743 34.823 15.8451 36.2679Z" fill="#6E5066"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M16.2762 51.1687C16.2762 51.1687 22.8396 49.7531 25.7043 47.4791C24.8183 43.8251 24.3985 40.0736 24.4551 36.3141C23.7445 40.7452 22.5753 43.7444 21.0227 42.8523C19.4702 41.9602 15.7524 29.5857 13.7598 33.3738C13.0458 43.7942 16.2762 51.1687 16.2762 51.1687Z" fill="#6E5066"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M45.2431 25.5919C46.6281 25.6389 54.4101 37.0746 54.0086 38.2973C53.6071 39.52 51.6425 38.5506 51.1498 37.6319C50.657 36.7131 44.0742 25.5513 45.2431 25.5919Z" fill="#6E5066"/>
</g>
<path d="M55.166 28.6107C56.0033 28.054 58.8159 33.2887 58.0283 34.0305C57.2406 34.7723 53.9785 29.4007 55.166 28.6107Z" fill="white"/>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M25.0038 31.433C25.0038 31.433 27.1926 18.5534 35.9288 19.8045C44.6649 21.0557 47.8483 32.276 47.8483 32.276C47.8483 32.276 43.1685 21.8534 36.1067 21.8692C26.2716 21.8989 25.0038 31.433 25.0038 31.433Z" fill="#6E5066"/>
</g>
<path d="M52.7781 19.5516C52.7781 19.5516 43.6111 6.4284 29.355 8.58635C29.355 8.58635 20.4111 11.5619 15.1843 24.9285C15.1843 24.9285 17.7118 12.9928 28.983 7.97835C28.9843 7.98336 42.0501 4.58426 52.7781 19.5516Z" fill="#513752"/>
<path d="M40.9289 17.4026C42.9206 17.5927 46.8151 20.4628 46.4417 21.1901C46.0683 21.9174 42.889 21.3164 41.1352 20.0332C39.5937 18.9023 39.482 17.2649 40.9289 17.4026Z" fill="#513752"/>
<path d="M44.2769 18.8919C44.2769 18.8919 46.9585 20.5162 46.5468 21.361C46.2364 22.0232 43.116 21.0419 43.116 21.0419C43.116 21.0419 45.2422 21.6141 45.701 20.8853C46.1598 20.1566 44.2769 18.8919 44.2769 18.8919Z" fill="#EED6DD"/>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M42.8411 18.0543C44.6726 19.0271 42.6645 20.6964 41.1897 19.7942C39.7148 18.8919 39.5995 17.8613 40.4271 17.5485C41.2546 17.2356 42.2833 17.7534 42.8411 18.0543Z" fill="#6E5066"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M28.0608 55.3243C28.7989 57.4586 29.2021 60.7442 26.5293 61.1333C23.8565 61.5224 22.7899 56.9617 23.3689 56.5814C23.9479 56.2011 28.0608 55.3243 28.0608 55.3243Z" fill="#912790"/>
</g>
<path d="M25.7134 54.5365C28.2492 53.9964 29.907 58.0823 26.2605 59.0594C23.1143 59.9025 21.4941 55.4365 25.7134 54.5365Z" fill="#4F0086"/>
<path d="M25.46 53.6876C27.9958 53.1475 29.6286 57.2402 26.0021 58.2119C22.8558 59.055 21.2357 54.589 25.46 53.6876Z" fill="#8B6ED1"/>
<path d="M27.8634 55.1676C27.3018 56.3528 23.2346 56.6599 23.071 56.0496C22.937 55.5494 23.1589 54.4767 24.9754 53.8131C26.7919 53.1494 28.0248 54.8295 27.8634 55.1676Z" fill="#B6D3FF"/>
<g opacity="0.4" style="mix-blend-mode:multiply">
<path d="M27.9582 56.2681C27.9115 57.074 26.6011 58.3259 25.1776 58.2355C23.7541 58.1451 22.8775 56.8145 23.3659 56.5765C23.8543 56.3384 27.9933 55.7387 27.9582 56.2681Z" fill="#4F0086"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M37.635 50.865C38.3744 53.0043 38.4697 56.2813 35.7956 56.6654C33.1214 57.0495 32.3655 52.5075 32.9431 52.1222C33.5207 51.7369 37.635 50.865 37.635 50.865Z" fill="#912790"/>
</g>
<path d="M35.2996 50.0968C37.8354 49.5567 39.4682 53.6494 35.8417 54.6211C32.6954 55.4642 31.0793 51.0132 35.2996 50.0968Z" fill="#4F0086"/>
<path d="M35.0421 49.2495C37.5766 48.7044 39.2094 52.7971 35.5829 53.7688C32.4366 54.6118 30.8164 50.1459 35.0421 49.2495Z" fill="#8B6ED1"/>
<path d="M37.4453 50.7228C36.8786 51.9093 32.8127 52.2214 32.6478 51.6062C32.5138 51.106 32.762 50.0316 34.5535 49.3746C36.345 48.7177 37.6016 50.386 37.4453 50.7228Z" fill="#B6D3FF"/>
<g opacity="0.4" style="mix-blend-mode:multiply">
<path d="M37.5448 51.8281C37.4931 52.6354 36.1812 53.8823 34.7578 53.7919C33.3343 53.7015 32.4641 52.3746 32.9461 52.1329C33.4282 51.8911 37.5735 51.295 37.5448 51.8281Z" fill="#4F0086"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M57.1762 45.1633C57.9107 47.3039 58.5626 50.4371 55.8934 50.8199C53.2243 51.2027 51.9017 46.8071 52.4843 46.4204C53.067 46.0338 57.1762 45.1633 57.1762 45.1633Z" fill="#912790"/>
</g>
<path d="M54.8314 44.3804C57.3672 43.8403 59 47.933 55.3735 48.9047C52.2472 49.7424 50.6057 45.2768 54.8314 44.3804Z" fill="#4F0086"/>
<path d="M54.5728 43.5286C57.1086 42.9886 58.7414 47.0812 55.1199 48.0516C51.9736 48.8946 50.3534 44.4287 54.5728 43.5286Z" fill="#8B6ED1"/>
<path d="M56.9804 45.007C56.4138 46.1935 52.3478 46.5056 52.183 45.8904C52.049 45.3902 52.2972 44.3157 54.0887 43.6588C55.8802 43.0018 57.1418 44.6689 56.9804 45.007Z" fill="#B6D3FF"/>
<g opacity="0.4" style="mix-blend-mode:multiply">
<path d="M57.075 46.1063C57.0246 46.9187 55.7128 48.1655 54.2943 48.0738C52.8759 47.982 51.9956 46.6578 52.4827 46.4147C52.9697 46.1716 57.11 45.5769 57.075 46.1063Z" fill="#4F0086"/>
</g>
<g opacity="0.5" style="mix-blend-mode:multiply">
<path d="M67.6777 44.424C68.4121 46.5647 68.4408 49.9131 65.7667 50.2972C63.0925 50.6814 62.4032 46.0678 62.9808 45.6825C63.5584 45.2972 67.6777 44.424 67.6777 44.424Z" fill="#912790"/>
</g>
<path d="M65.3311 43.6261C67.8669 43.086 69.4997 47.1787 65.8732 48.1504C62.7269 48.9934 61.1068 44.5275 65.3311 43.6261Z" fill="#4F0086"/>
<path d="M65.0729 42.779C67.6087 42.2389 69.2414 46.3316 65.62 47.3019C62.4737 48.145 60.8522 43.674 65.0729 42.779Z" fill="#8B6ED1"/>
<path d="M67.4779 44.2572C66.9099 45.4387 62.844 45.7508 62.6804 45.1406C62.5464 44.6404 62.7933 43.5609 64.5848 42.904C66.3763 42.247 67.6339 43.8991 67.4779 44.2572Z" fill="#B6D3FF"/>
<g opacity="0.4" style="mix-blend-mode:multiply">
<path d="M67.5744 45.3573C67.5227 46.1646 66.2109 47.4114 64.7887 47.326C63.3666 47.2407 62.4937 45.9037 62.9771 45.667C63.4605 45.4302 67.6081 44.8228 67.5744 45.3573Z" fill="#4F0086"/>
</g>
</g>
<defs>
<linearGradient id="paint0_linear_1_21189" x1="26.5399" y1="60.5238" x2="41.4172" y2="75.1299" gradientUnits="userSpaceOnUse">
<stop stop-color="#727A9D"/>
<stop offset="0.3" stop-color="#836888"/>
<stop offset="0.85" stop-color="#715379"/>
<stop offset="1" stop-color="#6D636A"/>
</linearGradient>
<linearGradient id="paint1_linear_1_21189" x1="61.7748" y1="60.9091" x2="25.8303" y2="65.5385" gradientUnits="userSpaceOnUse">
<stop stop-color="#B8A1A6"/>
<stop offset="0.26" stop-color="#B49EA4"/>
<stop offset="0.49" stop-color="#AA939F"/>
<stop offset="0.72" stop-color="#978195"/>
<stop offset="0.93" stop-color="#7E6888"/>
<stop offset="1" stop-color="#745E83"/>
</linearGradient>
<linearGradient id="paint2_linear_1_21189" x1="63.3906" y1="63.1731" x2="43.3725" y2="68.5369" gradientUnits="userSpaceOnUse">
<stop stop-color="#B8A1A6"/>
<stop offset="0.26" stop-color="#B49EA4"/>
<stop offset="0.49" stop-color="#AA939F"/>
<stop offset="0.72" stop-color="#978195"/>
<stop offset="0.93" stop-color="#7E6888"/>
<stop offset="1" stop-color="#745E83"/>
</linearGradient>
<linearGradient id="paint3_linear_1_21189" x1="58.9523" y1="53.6672" x2="35.8229" y2="59.8647" gradientUnits="userSpaceOnUse">
<stop stop-color="#3D069A"/>
<stop offset="1" stop-color="#6509BA"/>
</linearGradient>
<linearGradient id="paint4_linear_1_21189" x1="46.927" y1="59.6603" x2="43.4007" y2="46.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#902EF6"/>
<stop offset="1" stop-color="#682EFF"/>
</linearGradient>
<linearGradient id="paint5_linear_1_21189" x1="11.9914" y1="60.0586" x2="21.0935" y2="58.574" gradientUnits="userSpaceOnUse">
<stop stop-color="#4388E8"/>
<stop offset="0.28" stop-color="#57198A"/>
<stop offset="1" stop-color="#511881"/>
</linearGradient>
<linearGradient id="paint6_linear_1_21189" x1="55.8762" y1="20.6835" x2="11.1031" y2="32.6805" gradientUnits="userSpaceOnUse">
<stop stop-color="#C3B1B5"/>
<stop offset="0.12" stop-color="#A192A2"/>
<stop offset="0.26" stop-color="#827590"/>
<stop offset="0.37" stop-color="#706486"/>
<stop offset="0.45" stop-color="#695E82"/>
<stop offset="1" stop-color="#937B80"/>
</linearGradient>
<linearGradient id="paint7_linear_1_21189" x1="33.685" y1="73.7338" x2="52.2916" y2="64.6629" gradientUnits="userSpaceOnUse">
<stop stop-color="#B8A1A6"/>
<stop offset="0.26" stop-color="#B49EA4"/>
<stop offset="0.49" stop-color="#AA939F"/>
<stop offset="0.72" stop-color="#978195"/>
<stop offset="0.93" stop-color="#7E6888"/>
<stop offset="1" stop-color="#745E83"/>
</linearGradient>
<clipPath id="clip0_1_21189">
<rect width="60.8522" height="77.9153" fill="white" transform="translate(0 15.75) rotate(-15)"/>
</clipPath>
</defs>
</svg>

2
lib/common_ui/resources/my_assets.dart

@ -49,6 +49,7 @@ class MyAssets {
static const String button2 = 'assets/svg/button_2.svg.vec'; static const String button2 = 'assets/svg/button_2.svg.vec';
static const String button2Tablet = 'assets/svg/button_2_tablet.svg.vec'; static const String button2Tablet = 'assets/svg/button_2_tablet.svg.vec';
static const String button3 = 'assets/svg/button_3.svg.vec'; static const String button3 = 'assets/svg/button_3.svg.vec';
static const String button35 = 'assets/svg/button_3.5.svg.vec';
static const String button3Tablet = 'assets/svg/button_3_tablet.svg.vec'; static const String button3Tablet = 'assets/svg/button_3_tablet.svg.vec';
static const String theme = 'assets/svg/theme.svg.vec'; static const String theme = 'assets/svg/theme.svg.vec';
static const String facebook = 'assets/svg/facebook.svg.vec'; static const String facebook = 'assets/svg/facebook.svg.vec';
@ -74,6 +75,7 @@ class MyAssets {
static const String iconNotif = 'assets/svg/icon_notif.svg.vec'; static const String iconNotif = 'assets/svg/icon_notif.svg.vec';
static const String iconPlayVideo = 'assets/svg/icon_play_video.svg.vec'; static const String iconPlayVideo = 'assets/svg/icon_play_video.svg.vec';
static const String check = 'assets/svg/check.svg.vec'; static const String check = 'assets/svg/check.svg.vec';
static const String lock = 'assets/svg/lock.svg.vec';
static final List<String> images = [ static final List<String> images = [
backgroundHome, backgroundHome,

43
lib/common_ui/resources/my_audios.dart

@ -1,6 +1,13 @@
import 'dart:math';
import 'package:hadi_hoda_flutter/core/constants/my_constants.dart';
import 'package:hadi_hoda_flutter/core/utils/local_storage.dart';
class MyAudios { class MyAudios {
static const MyAudios _i = MyAudios._internal(); static const MyAudios _i = MyAudios._internal();
const MyAudios._internal(); const MyAudios._internal();
factory MyAudios() => _i; factory MyAudios() => _i;
static const String back = 'assets/audios/back.mp3'; static const String back = 'assets/audios/back.mp3';
@ -8,7 +15,41 @@ class MyAudios {
static const String diamondEnd = 'assets/audios/diamond_end.mp3'; static const String diamondEnd = 'assets/audios/diamond_end.mp3';
static const String diamondIncrease = 'assets/audios/diamond_increase.mp3'; static const String diamondIncrease = 'assets/audios/diamond_increase.mp3';
static const String home = 'assets/audios/home.mp3'; static const String home = 'assets/audios/home.mp3';
static const String incorrectAnswer = 'assets/audios/incorrect_answer.mp3';
static const String question = 'assets/audios/question.mp3'; static const String question = 'assets/audios/question.mp3';
static const String rightAnswer = 'assets/audios/right_answer.mp3'; static const String rightAnswer = 'assets/audios/right_answer.mp3';
static String get incorrectAnswer {
final currentLang =
LocalStorage.readData(key: MyConstants.selectLanguage) ??
MyConstants.defaultLanguage;
if(_incorrectAnswers.keys.contains(currentLang)) {
final audios = _incorrectAnswers[currentLang];
return audios![Random().nextInt(audios.length)];
}
return 'assets/audios/incorrect_answer.mp3';
}
static const _incorrectAnswers = {
'en': [
"assets/audios/incorrect_answers/en_1.mp3",
"assets/audios/incorrect_answers/en_2.mp3",
"assets/audios/incorrect_answers/en_3.mp3",
"assets/audios/incorrect_answers/en_4.mp3",
],
'fa': [
"assets/audios/incorrect_answers/fa_1.mp3",
"assets/audios/incorrect_answers/fa_2.mp3",
"assets/audios/incorrect_answers/fa_3.mp3",
"assets/audios/incorrect_answers/fa_4.mp3",
"assets/audios/incorrect_answers/fa_5.mp3",
"assets/audios/incorrect_answers/fa_6.mp3",
],
'ar': [
"assets/audios/incorrect_answers/ar_1.mp3",
"assets/audios/incorrect_answers/ar_2.mp3",
"assets/audios/incorrect_answers/ar_3.mp3",
"assets/audios/incorrect_answers/ar_4.mp3",
"assets/audios/incorrect_answers/ar_5.mp3",
],
};
} }

135
lib/common_ui/resources/my_text_style.dart

@ -1,90 +1,137 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart';
import 'package:hadi_hoda_flutter/core/constants/my_constants.dart';
import 'package:hadi_hoda_flutter/core/utils/local_storage.dart';
import 'package:hadi_hoda_flutter/core/utils/my_device.dart';
import 'package:hadi_hoda_flutter/core/utils/my_device.dart';
import 'package:hadi_hoda_flutter/core/utils/my_device.dart';
import 'package:hadi_hoda_flutter/core/utils/my_device.dart';
import 'package:hadi_hoda_flutter/core/utils/my_device.dart';
import 'package:hadi_hoda_flutter/core/utils/my_device.dart';
import 'package:hadi_hoda_flutter/core/utils/my_device.dart';
import 'package:hadi_hoda_flutter/core/utils/my_device.dart';
import 'package:hadi_hoda_flutter/core/utils/my_device.dart';
import 'package:hadi_hoda_flutter/core/utils/my_device.dart';
import 'package:hadi_hoda_flutter/core/utils/my_device.dart';
class MYTextStyle { class MYTextStyle {
static const MYTextStyle _i = MYTextStyle._internal(); static const MYTextStyle _i = MYTextStyle._internal();
const MYTextStyle._internal(); const MYTextStyle._internal();
factory MYTextStyle() => _i; factory MYTextStyle() => _i;
static const String dinoKids = 'DinoKids'; static const String dinoKids = 'DinoKids';
static const String notoSansArabic = 'NotoSansArabic';
static const String marhey = 'Marhey'; static const String marhey = 'Marhey';
static const String baloo2 = 'Baloo_2'; static const String baloo2 = 'Baloo_2';
static const Color textColor = MyColors.white; static const Color textColor = MyColors.white;
static const TextStyle titr0 = TextStyle(
fontFamily: marhey,
fontWeight: FontWeight.w700,
fontSize: 26,
static bool _isArabicOrPersianLanguage() {
final selectedLanguage =
(LocalStorage.readData(key: MyConstants.selectLanguage) ??
MyConstants.defaultLanguage)
.toString()
.trim()
.toLowerCase();
final languageCode = selectedLanguage.split(RegExp(r'[_-]')).first;
return languageCode == 'ar' || languageCode == 'fa';
}
static String _fontForLanguage(String defaultFont) {
return _isArabicOrPersianLanguage() ? notoSansArabic : defaultFont;
}
static FontWeight _fontWeightForLanguage(FontWeight defaultWeight) {
if (!_isArabicOrPersianLanguage()) return defaultWeight;
return switch (defaultWeight) {
FontWeight.w100 => FontWeight.w300,
FontWeight.w200 => FontWeight.w400,
FontWeight.w300 => FontWeight.w500,
FontWeight.w400 => FontWeight.w600,
FontWeight.w500 => FontWeight.w700,
FontWeight.w600 => FontWeight.w800,
FontWeight.w700 => FontWeight.w900,
FontWeight.w800 => FontWeight.w900,
FontWeight.w900 => FontWeight.w900,
_ => defaultWeight,
};
}
static TextStyle get titr0 => TextStyle(
fontFamily: _fontForLanguage(marhey),
fontWeight: _fontWeightForLanguage(FontWeight.w700),
fontSize: MyDevice.fontSize(26),
color: textColor, color: textColor,
); );
static const TextStyle titr1 = TextStyle(
fontFamily: baloo2,
fontWeight: FontWeight.w800,
fontSize: 20,
static TextStyle get titr1 => TextStyle(
fontFamily: _fontForLanguage(baloo2),
fontWeight: _fontWeightForLanguage(FontWeight.w800),
fontSize: MyDevice.fontSize(20),
color: textColor, color: textColor,
); );
static const TextStyle titr3 = TextStyle(
fontFamily: marhey,
fontWeight: FontWeight.w600,
fontSize: 18,
static TextStyle get titr3 => TextStyle(
fontFamily: _fontForLanguage(marhey),
fontWeight: _fontWeightForLanguage(FontWeight.w600),
fontSize: MyDevice.fontSize(18),
color: textColor, color: textColor,
); );
static const TextStyle titr4 = TextStyle(
fontFamily: baloo2,
fontWeight: FontWeight.w700,
fontSize: 14,
static TextStyle get titr4 => TextStyle(
fontFamily: _fontForLanguage(marhey),
fontWeight: _fontWeightForLanguage(FontWeight.w700),
fontSize: MyDevice.fontSize(14),
color: textColor, color: textColor,
); );
static const TextStyle matn1 = TextStyle(
fontFamily: baloo2,
fontWeight: FontWeight.w600,
fontSize: 18,
static TextStyle get matn1 => TextStyle(
fontFamily: _fontForLanguage(baloo2),
fontWeight: _fontWeightForLanguage(FontWeight.w600),
fontSize: MyDevice.fontSize(18),
color: textColor, color: textColor,
); );
static const TextStyle matn2_3 = TextStyle(
fontFamily: baloo2,
fontWeight: FontWeight.w700,
fontSize: 18,
static TextStyle get matn2_3 => TextStyle(
fontFamily: _fontForLanguage(baloo2),
fontWeight: _fontWeightForLanguage(FontWeight.w700),
fontSize: MyDevice.fontSize(18),
color: textColor, color: textColor,
); );
static const TextStyle matn2_2 = TextStyle(
fontFamily: baloo2,
fontWeight: FontWeight.w700,
fontSize: 16,
static TextStyle get matn2_2 => TextStyle(
fontFamily: _fontForLanguage(baloo2),
fontWeight: _fontWeightForLanguage(FontWeight.w700),
fontSize: MyDevice.fontSize(16),
color: textColor, color: textColor,
); );
static const TextStyle matn2 = TextStyle(
fontFamily: baloo2,
fontWeight: FontWeight.w700,
fontSize: 14,
static TextStyle get matn2 => TextStyle(
fontFamily: _fontForLanguage(baloo2),
fontWeight: _fontWeightForLanguage(FontWeight.w700),
fontSize: MyDevice.fontSize(14),
color: textColor, color: textColor,
); );
static const TextStyle matn3 = TextStyle(
fontFamily: baloo2,
fontWeight: FontWeight.w500,
fontSize: 12,
static TextStyle get matn3 => TextStyle(
fontFamily: _fontForLanguage(baloo2),
fontWeight: _fontWeightForLanguage(FontWeight.w500),
fontSize: MyDevice.fontSize(12),
color: textColor, color: textColor,
); );
static const TextStyle button1 = TextStyle(
fontFamily: dinoKids,
fontWeight: FontWeight.w400,
fontSize: 45,
static TextStyle get button1 => TextStyle(
fontFamily: _fontForLanguage(dinoKids),
fontWeight: _fontWeightForLanguage(FontWeight.w400),
fontSize: MyDevice.fontSize(45),
color: textColor, color: textColor,
); );
static const TextStyle button2 = TextStyle(
fontFamily: dinoKids,
fontWeight: FontWeight.w400,
fontSize: 30,
static TextStyle get button2 => TextStyle(
fontFamily: _fontForLanguage(dinoKids),
fontWeight: _fontWeightForLanguage(FontWeight.w400),
fontSize: MyDevice.fontSize(30),
color: textColor, color: textColor,
); );
} }

10
lib/core/middlewares/my_middlewares.dart

@ -31,6 +31,16 @@ class MyMiddlewares {
} }
} }
static FutureOr<String?> download(BuildContext context, GoRouterState state) {
final String? firstIntro = LocalStorage.readData(
key: MyConstants.firstIntro);
if (firstIntro != 'true') {
return Routes.introPage;
} else {
return null;
}
}
static FutureOr<String?> question(BuildContext context, GoRouterState state) { static FutureOr<String?> question(BuildContext context, GoRouterState state) {
final String? firstShowCase = LocalStorage.readData( final String? firstShowCase = LocalStorage.readData(
key: MyConstants.firstShowcase); key: MyConstants.firstShowcase);

3
lib/core/network/interceptors/logging_interceptor.dart

@ -10,7 +10,8 @@ class LoggingInterceptor {
requestHeader: true, requestHeader: true,
requestBody: true, requestBody: true,
responseBody: true, responseBody: true,
responseHeader: false,
responseHeader: true,
request: true,
error: true, error: true,
compact: true, compact: true,
maxWidth: 90, maxWidth: 90,

5
lib/core/params/level_params.dart

@ -1,13 +1,16 @@
class LevelParams { class LevelParams {
int? id; int? id;
bool forceRemote;
LevelParams({this.id});
LevelParams({this.id, this.forceRemote = false});
LevelParams copyWith({ LevelParams copyWith({
int? id, int? id,
bool? forceRemote,
}) { }) {
return LevelParams( return LevelParams(
id: id ?? this.id, id: id ?? this.id,
forceRemote: forceRemote ?? this.forceRemote,
); );
} }
} }

3
lib/core/routers/my_routes.dart

@ -74,6 +74,7 @@ GoRouter _appPages() => GoRouter(
GoRoute( GoRoute(
name: Routes.downloadPage, name: Routes.downloadPage,
path: Routes.downloadPage, path: Routes.downloadPage,
redirect: MyMiddlewares.download,
pageBuilder: (context, state) { pageBuilder: (context, state) {
final config = state.extra as DownloadPageConfig; final config = state.extra as DownloadPageConfig;
return myPageTransition( return myPageTransition(
@ -132,7 +133,7 @@ GoRouter _appPages() => GoRouter(
locator(), locator(),
locator(instanceName: MyConstants.mainAudioService), locator(instanceName: MyConstants.mainAudioService),
locator(instanceName: MyConstants.effectAudioService), locator(instanceName: MyConstants.effectAudioService),
locator()
locator(),
)..add(SetCurrentLevelEvent()), )..add(SetCurrentLevelEvent()),
child: const LevelPage(), child: const LevelPage(),
), ),

7
lib/core/services/audio_service.dart

@ -6,6 +6,7 @@ import 'package:just_audio/just_audio.dart';
class AudioService { class AudioService {
final AudioPlayer _player = AudioPlayer(); final AudioPlayer _player = AudioPlayer();
final StreamController<bool> _streamController = StreamController.broadcast(); final StreamController<bool> _streamController = StreamController.broadcast();
static const double _muteThreshold = 0.001;
final double volume; final double volume;
AudioService({required this.volume}){ AudioService({required this.volume}){
@ -74,8 +75,9 @@ class AudioService {
Future<void> changeMute({double? newVolume}) async { Future<void> changeMute({double? newVolume}) async {
try { try {
if (_player.volume == 0) {
await _player.setVolume(newVolume ?? volume);
if (isMuted) {
final targetVolume = (newVolume ?? volume).clamp(0.0, 1.0).toDouble();
await _player.setVolume(targetVolume);
} else { } else {
await _player.setVolume(0); await _player.setVolume(0);
} }
@ -99,6 +101,7 @@ class AudioService {
Stream<double> volumeStream() => _player.volumeStream; Stream<double> volumeStream() => _player.volumeStream;
double get audioVolume => _player.volume; double get audioVolume => _player.volume;
bool get isMuted => _player.volume <= _muteThreshold;
Stream<bool> playingStream() async* { Stream<bool> playingStream() async* {
_player.processingStateStream.listen((event) { _player.processingStateStream.listen((event) {

3
lib/core/utils/app_life_cycle.dart

@ -4,6 +4,8 @@ import 'package:hadi_hoda_flutter/core/services/audio_service.dart';
import 'package:hadi_hoda_flutter/init_bindings.dart'; import 'package:hadi_hoda_flutter/init_bindings.dart';
class AppLifeCycleController extends WidgetsBindingObserver { class AppLifeCycleController extends WidgetsBindingObserver {
static bool suppressAutoResumeAudio = false;
AppLifeCycleController() { AppLifeCycleController() {
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
} }
@ -25,6 +27,7 @@ class AppLifeCycleController extends WidgetsBindingObserver {
mainAudio.pause(); mainAudio.pause();
effect.pause(); effect.pause();
} else if (state == AppLifecycleState.resumed) { } else if (state == AppLifecycleState.resumed) {
if (suppressAutoResumeAudio) return;
mainAudio.play(); mainAudio.play();
effect.play(); effect.play();
} }

18
lib/core/utils/my_device.dart

@ -1,6 +1,7 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:hadi_hoda_flutter/core/utils/my_context.dart';
class MyDevice { class MyDevice {
static const MyDevice _i = MyDevice._internal(); static const MyDevice _i = MyDevice._internal();
@ -26,6 +27,23 @@ class MyDevice {
return MediaQuery.sizeOf(context).width >= 1200; return MediaQuery.sizeOf(context).width >= 1200;
} }
static double fontSize(
double size, {
double mobileScale = 1,
double tabletScale = 1.25,
double desktopScale = 1.35,
}) {
if (isTablet(MyContext.get)) {
return size * tabletScale;
}
if (isDesktop(MyContext.get)) {
return size * desktopScale;
}
return size * mobileScale;
}
/// Get device size /// Get device size
static double? getDeviceWidth(BuildContext context) { static double? getDeviceWidth(BuildContext context) {
if (isMobile(context)) { if (isMobile(context)) {

16
lib/core/utils/screen_size.dart

@ -11,3 +11,19 @@ extension AdaptiveSize on double {
double get w => MyContext.get.widthScreen * this; double get w => MyContext.get.widthScreen * this;
double get h => MyContext.get.heightScreen * this; double get h => MyContext.get.heightScreen * this;
} }
extension SizeExtension on num {
double get h {
final screenHeight = MyContext.get.heightScreen;
const designHeight = 812; // your design height (example: iPhone X)
return (this * screenHeight) / designHeight;
}
double w(BuildContext context) {
final screenWidth = MediaQuery.sizeOf(context).width;
const designWidth = 375; // your design width
return (this * screenWidth) / designWidth;
}
}

4
lib/core/widgets/animations/fade_anim.dart

@ -19,8 +19,8 @@ class _FadeAnimState extends State<FadeAnim>
super.initState(); super.initState();
_controller = AnimationController( _controller = AnimationController(
vsync: this, vsync: this,
duration: const Duration(milliseconds: 500),
reverseDuration: const Duration(seconds: 500),
duration: const Duration(milliseconds: 200),
reverseDuration: const Duration(seconds: 200),
); );
_animation = Tween<double>( _animation = Tween<double>(
begin: 0, begin: 0,

4
lib/core/widgets/animations/fade_anim_delayed.dart

@ -24,8 +24,8 @@ class _FadeAnimDelayedState extends State<FadeAnimDelayed>
super.initState(); super.initState();
_controller = AnimationController( _controller = AnimationController(
vsync: this, vsync: this,
duration: const Duration(milliseconds: 500),
reverseDuration: const Duration(seconds: 500),
duration: const Duration(milliseconds: 200),
reverseDuration: const Duration(seconds: 200),
); );
_animation = Tween<double>( _animation = Tween<double>(
begin: 0, begin: 0,

8
lib/core/widgets/animations/slide_anim.dart

@ -20,10 +20,10 @@ class _SlideAnimState extends State<SlideAnim>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
late Animation<Offset> _animation; late Animation<Offset> _animation;
final List<Offset> offsetList = const [ final List<Offset> offsetList = const [
Offset(-2, -2),
Offset(2, -2),
Offset(-2, 2),
Offset(2, 2),
Offset(-3, -3),
Offset(3, -3),
Offset(-3, 3),
Offset(3, 3),
]; ];
@override @override

4
lib/core/widgets/animations/slide_down_fade.dart

@ -25,8 +25,8 @@ class _SlideDownFadeState extends State<SlideDownFade>
super.initState(); super.initState();
_controller = AnimationController( _controller = AnimationController(
vsync: this, vsync: this,
duration: const Duration(milliseconds: 500),
reverseDuration: const Duration(milliseconds: 500),
duration: const Duration(milliseconds: 200),
reverseDuration: const Duration(milliseconds: 200),
); );
_fadeAnim = Tween<double>( _fadeAnim = Tween<double>(

4
lib/core/widgets/animations/slide_up_fade.dart

@ -25,8 +25,8 @@ class _SlideUpFadeState extends State<SlideUpFade>
super.initState(); super.initState();
_controller = AnimationController( _controller = AnimationController(
vsync: this, vsync: this,
duration: const Duration(milliseconds: 500),
reverseDuration: const Duration(milliseconds: 500),
duration: const Duration(milliseconds: 200),
reverseDuration: const Duration(milliseconds: 200),
); );
_fadeAnim = Tween<double>( _fadeAnim = Tween<double>(

4
lib/core/widgets/answer_box/answer_box.dart

@ -70,13 +70,13 @@ class _AnswerBoxState extends State<AnswerBox> {
bottom: 0, bottom: 0,
child: AnswerTextBox(text: widget.answer.title ?? ''), child: AnswerTextBox(text: widget.answer.title ?? ''),
), ),
PositionedDirectional(
if(widget.answer.audioInfo?.filename?.isNotEmpty == true)Positioned(
top: setSize( top: setSize(
context: context, context: context,
mobile: MySpaces.s12, mobile: MySpaces.s12,
tablet: MySpaces.s20, tablet: MySpaces.s20,
), ),
end: setSize(
right: setSize(
context: context, context: context,
mobile: MySpaces.s8, mobile: MySpaces.s8,
tablet: MySpaces.s20, tablet: MySpaces.s20,

19
lib/core/widgets/answer_box/answer_box_show.dart

@ -41,18 +41,29 @@ class AnswerBoxShow extends StatelessWidget {
left: 0, left: 0,
right: 0, right: 0,
bottom: -10, bottom: -10,
child: AnswerTextBox(text: answer.title ?? ''),
child: AnswerTextBox(
text: answer.title ?? '',
padding: const EdgeInsetsGeometry.all(14),
), ),
PositionedDirectional(
),
Positioned(
top: setSize(context: context, mobile: MySpaces.s30, tablet: 60), top: setSize(context: context, mobile: MySpaces.s30, tablet: 60),
end: setSize(context: context, mobile: MySpaces.s20, tablet: 60),
right: setSize(
context: context,
mobile: MySpaces.s20,
tablet: 60,
),
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
onNotifTap?.call(answer); onNotifTap?.call(answer);
}, },
child: MyImage( child: MyImage(
image: MyAssets.iconNotif, image: MyAssets.iconNotif,
size: setSize(context: context, mobile: MySpaces.s40, tablet: 60),
size: setSize(
context: context,
mobile: MySpaces.s40,
tablet: 60,
),
), ),
), ),
), ),

11
lib/core/widgets/answer_box/styles/text_box.dart

@ -1,9 +1,11 @@
import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
class AnswerTextBox extends StatelessWidget { class AnswerTextBox extends StatelessWidget {
const AnswerTextBox({super.key, required this.text});
final EdgeInsetsGeometry? padding;
const AnswerTextBox({super.key, required this.text, this.padding});
final String text; final String text;
@ -13,7 +15,7 @@ class AnswerTextBox extends StatelessWidget {
clipper: WavyBannerClipper(), clipper: WavyBannerClipper(),
child: Container( child: Container(
height: 90, height: 90,
padding: const EdgeInsets.symmetric(horizontal: MySpaces.s10),
padding: padding ?? const EdgeInsets.symmetric(horizontal: MySpaces.s10),
alignment: Alignment.center, alignment: Alignment.center,
decoration: const BoxDecoration( decoration: const BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
@ -25,14 +27,17 @@ class AnswerTextBox extends StatelessWidget {
], ],
), ),
), ),
child: Text(
child: AutoSizeText(
text, text,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: MYTextStyle.matn2.copyWith( style: MYTextStyle.matn2.copyWith(
color: const Color(0XFF322386), color: const Color(0XFF322386),
height: 1.2, height: 1.2,
), ),
minFontSize: 8,
stepGranularity: 0.5,
maxLines: 5, maxLines: 5,
overflow: TextOverflow.ellipsis,
), ),
), ),
); );

13
lib/core/widgets/button/my_blue_button.dart

@ -13,11 +13,14 @@ class MyBlueButton extends StatelessWidget {
this.onTap, this.onTap,
this.title, this.title,
this.top, this.top,
this.textStyle,
}); });
final VoidCallback? onTap; final VoidCallback? onTap;
final String? title; final String? title;
final double? top; final double? top;
final TextStyle? textStyle;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -38,13 +41,21 @@ class MyBlueButton extends StatelessWidget {
), ),
PositionedDirectional( PositionedDirectional(
top: top ?? setSize(context: context, mobile: MySpaces.s6, tablet: MySpaces.s20), top: top ?? setSize(context: context, mobile: MySpaces.s6, tablet: MySpaces.s20),
child: SizedBox(
width: 170,
height: 62,
child: FittedBox(
child: Text( child: Text(
title ?? '', title ?? '',
style: MYTextStyle.button1.copyWith(
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: textStyle ?? MYTextStyle.button1.copyWith(
color: const Color(0XFF1D6EFF), color: const Color(0XFF1D6EFF),
), ),
), ),
), ),
),
),
], ],
), ),
); );

4
lib/core/widgets/button/my_white_button.dart

@ -13,11 +13,13 @@ class MyWhiteButton extends StatelessWidget {
this.onTap, this.onTap,
this.title, this.title,
this.top, this.top,
this.bgSvgMobile,
}); });
final VoidCallback? onTap; final VoidCallback? onTap;
final String? title; final String? title;
final double? top; final double? top;
final String? bgSvgMobile;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -31,7 +33,7 @@ class MyWhiteButton extends StatelessWidget {
MyImage( MyImage(
image: setSize( image: setSize(
context: context, context: context,
mobile: MyAssets.button3,
mobile: bgSvgMobile ?? MyAssets.button3,
tablet: MyAssets.button3Tablet, tablet: MyAssets.button3Tablet,
) ?? '', ) ?? '',
), ),

50
lib/core/widgets/dialog/hadith_dialog.dart

@ -38,11 +38,8 @@ class HadithDialog extends StatelessWidget {
child: Center( child: Center(
child: Padding( child: Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: setSize(
context: context,
mobile: 18,
tablet: 120,
) ?? 0,
horizontal:
setSize(context: context, mobile: 18, tablet: 120) ?? 0,
), ),
child: Stack( child: Stack(
clipBehavior: Clip.none, clipBehavior: Clip.none,
@ -50,8 +47,10 @@ class HadithDialog extends StatelessWidget {
DialogBackground( DialogBackground(
child: Builder( child: Builder(
builder: (context) { builder: (context) {
if(hadith.isNotEmpty){
return ListView.separated(
if (hadith.isNotEmpty) {
return Scrollbar(
child: ListView.separated(
padding: const EdgeInsets.symmetric(horizontal: 11),
itemCount: hadith.length, itemCount: hadith.length,
separatorBuilder: (context, index) => const Divider( separatorBuilder: (context, index) => const Divider(
height: 40, height: 40,
@ -60,19 +59,52 @@ class HadithDialog extends StatelessWidget {
indent: MySpaces.s20, indent: MySpaces.s20,
color: Color(0xFFC2BDE4), color: Color(0xFFC2BDE4),
), ),
itemBuilder: (context, index) => Text.rich(
itemBuilder: (context, index) => Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text.rich(
textDirection: TextDirection.rtl,
TextSpan( TextSpan(
text: '${hadith[index].narratorName ?? ''}:\n',
text:
'${hadith[index].narratorName ?? ''}:\n',
style: MYTextStyle.titr1.copyWith( style: MYTextStyle.titr1.copyWith(
color: const Color(0XFF494178), color: const Color(0XFF494178),
fontFamily: MYTextStyle.notoSansArabic,
), ),
children: [ children: [
TextSpan( TextSpan(
text: hadith[index].hadithText, text: hadith[index].hadithText,
style: MYTextStyle.matn1.copyWith( style: MYTextStyle.matn1.copyWith(
color: const Color(0XFF494178), color: const Color(0XFF494178),
fontFamily: MYTextStyle.notoSansArabic,
fontWeight: FontWeight.w600,
fontSize: 16
),
),
],
),
),
if(hadith[index].translation?.isNotEmpty == true) ...[
const SizedBox(height: 6),
Text(
hadith[index].translation!,
style: MYTextStyle.matn1.copyWith(
color: const Color(0XFF494178),
fontWeight: FontWeight.w600,
fontSize: 16
),
),
], if(hadith[index].sourceName?.isNotEmpty == true) ...[
const SizedBox(height: 6),
Text(
hadith[index].sourceName!,
style: MYTextStyle.matn1.copyWith(
color: const Color(0XFF494178).withValues(alpha: .5),
fontWeight: FontWeight.w600,
fontSize: 12
), ),
), ),
]
], ],
), ),
), ),

25
lib/core/widgets/dialog/reward_dialog.dart

@ -1,5 +1,6 @@
import 'dart:ui'; import 'dart:ui';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_animations.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_animations.dart';
@ -19,6 +20,16 @@ Future<void> showRewardDialog({
required BuildContext context, required BuildContext context,
required PrizeEntity prize, required PrizeEntity prize,
}) async { }) async {
final result = await Connectivity().checkConnectivity();
if (!context.mounted) return;
if (result.contains(ConnectivityResult.none)) {
final messenger = ScaffoldMessenger.of(
context,
);
messenger.removeCurrentSnackBar();
messenger.showSnackBar(SnackBar(content: Text(context.translate.lost_connection)));
return;
}
await showDialog( await showDialog(
context: context, context: context,
builder: (context) => RewardDialog(prize: prize), builder: (context) => RewardDialog(prize: prize),
@ -89,12 +100,16 @@ class RewardDialog extends StatelessWidget {
children: [ children: [
Image.network( Image.network(
prize.imageURL ?? '', prize.imageURL ?? '',
errorBuilder: (context, error, stackTrace) => Container(
errorBuilder:
(context, error, stackTrace) =>
Container(
height: 300, height: 300,
width: 300, width: 300,
color: const Color(0XFFE0E0E0), color: const Color(0XFFE0E0E0),
), ),
loadingBuilder: (context, child, loadingProgress) => Container(
loadingBuilder:
(context, child, loadingProgress) =>
Container(
height: 300, height: 300,
width: 300, width: 300,
color: const Color(0XFFE0E0E0), color: const Color(0XFFE0E0E0),
@ -121,7 +136,11 @@ class RewardDialog extends StatelessWidget {
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
child: MyImage( child: MyImage(
image: MyAssets.closeBtn, image: MyAssets.closeBtn,
size: setSize(context: context, mobile: 40, tablet: 60),
size: setSize(
context: context,
mobile: 40,
tablet: 60,
),
), ),
), ),
), ),

11
lib/core/widgets/error/error_state.dart

@ -15,12 +15,16 @@ class ErrorState extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final retryTitle = context.translate.retry;
return Column( return Column(
children: [ children: [
const Spacer(), const Spacer(),
Text( Text(
context.translate.lost_connection, context.translate.lost_connection,
style: MYTextStyle.button1,
style: MYTextStyle.button1.copyWith(fontSize: 42),
maxLines: 1,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
), ),
const MyImage(image: MyAssets.error), const MyImage(image: MyAssets.error),
MySpaces.s40.gapHeight, MySpaces.s40.gapHeight,
@ -31,9 +35,12 @@ class ErrorState extends StatelessWidget {
), ),
const Spacer(), const Spacer(),
MyBlueButton( MyBlueButton(
title: context.translate.retry,
title: retryTitle,
onTap: onTap, onTap: onTap,
top: setSize(context: context, mobile: MySpaces.s6, tablet: MySpaces.s16), top: setSize(context: context, mobile: MySpaces.s6, tablet: MySpaces.s16),
textStyle: MYTextStyle.button1.copyWith(
color: const Color(0XFF1D6EFF),
),
), ),
], ],
); );

28
lib/core/widgets/page_transition/my_page_transition.dart

@ -7,11 +7,33 @@ CustomTransitionPage<dynamic> myPageTransition({
}) { }) {
return CustomTransitionPage( return CustomTransitionPage(
key: key, key: key,
transitionDuration: const Duration(milliseconds: 200),
reverseTransitionDuration: const Duration(milliseconds: 180),
child: child, child: child,
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
FadeTransition(
opacity: animation,
transitionsBuilder: (context, animation, secondaryAnimation, child) {
final curvedAnimation = CurvedAnimation(
parent: animation,
curve: Curves.fastOutSlowIn,
);
// Simple Android-like Material transition: fade + slight scale
final opacityAnimation = Tween<double>(
begin: 0.7,
end: 1.0,
).animate(curvedAnimation);
final scaleAnimation = Tween<double>(
begin: 0.96,
end: 1.0,
).animate(curvedAnimation);
return FadeTransition(
opacity: opacityAnimation,
child: ScaleTransition(
scale: scaleAnimation,
child: child, child: child,
), ),
); );
},
);
} }

42
lib/core/widgets/video/my_video_player.dart

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart';
import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/constants/my_constants.dart';
import 'package:hadi_hoda_flutter/core/services/audio_service.dart'; import 'package:hadi_hoda_flutter/core/services/audio_service.dart';
import 'package:hadi_hoda_flutter/core/utils/app_life_cycle.dart';
import 'package:hadi_hoda_flutter/core/utils/my_device.dart'; import 'package:hadi_hoda_flutter/core/utils/my_device.dart';
import 'package:hadi_hoda_flutter/init_bindings.dart'; import 'package:hadi_hoda_flutter/init_bindings.dart';
import 'package:pod_player/pod_player.dart'; import 'package:pod_player/pod_player.dart';
@ -16,7 +17,8 @@ class MyVideoPlayer extends StatefulWidget {
} }
class _MyVideoPlayerState extends State<MyVideoPlayer> { class _MyVideoPlayerState extends State<MyVideoPlayer> {
late final PodPlayerController _controller;
PodPlayerController? _controller;
bool _isPlayerReady = false;
final AudioService _mainAudioService = locator( final AudioService _mainAudioService = locator(
instanceName: MyConstants.mainAudioService, instanceName: MyConstants.mainAudioService,
); );
@ -27,8 +29,17 @@ class _MyVideoPlayerState extends State<MyVideoPlayer> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_mainAudioService.stop();
_effectAudioService.stop();
_initVideoSession();
}
Future<void> _initVideoSession() async {
AppLifeCycleController.suppressAutoResumeAudio = true;
await MyDevice.setLandscape();
await Future.wait([
_mainAudioService.pause(),
_effectAudioService.pause(),
]);
if (!mounted) return;
_controller = PodPlayerController( _controller = PodPlayerController(
podPlayerConfig: const PodPlayerConfig( podPlayerConfig: const PodPlayerConfig(
autoPlay: false, autoPlay: false,
@ -37,13 +48,17 @@ class _MyVideoPlayerState extends State<MyVideoPlayer> {
), ),
playVideoFrom: PlayVideoFrom.network(widget.videoURL ?? ''), playVideoFrom: PlayVideoFrom.network(widget.videoURL ?? ''),
)..initialise(); )..initialise();
if (!mounted) return;
setState(() {
_isPlayerReady = true;
});
} }
@override @override
void dispose() { void dispose() {
_controller.dispose();
_controller?.dispose();
AppLifeCycleController.suppressAutoResumeAudio = false;
_mainAudioService.play(); _mainAudioService.play();
_effectAudioService.play();
MyDevice.setPortrait(); MyDevice.setPortrait();
super.dispose(); super.dispose();
} }
@ -57,19 +72,20 @@ class _MyVideoPlayerState extends State<MyVideoPlayer> {
backgroundColor: MyColors.transparent, backgroundColor: MyColors.transparent,
foregroundColor: MyColors.white, foregroundColor: MyColors.white,
), ),
body: PodVideoPlayer(
controller: _controller,
body: _isPlayerReady
? PodVideoPlayer(
controller: _controller!,
matchVideoAspectRatioToFrame: true, matchVideoAspectRatioToFrame: true,
matchFrameAspectRatioToVideo: true, matchFrameAspectRatioToVideo: true,
videoAspectRatio: _controller.videoPlayerValue?.aspectRatio ?? 16 / 9,
videoAspectRatio:
_controller?.videoPlayerValue?.aspectRatio ?? 16 / 9,
podProgressBarConfig: const PodProgressBarConfig(), podProgressBarConfig: const PodProgressBarConfig(),
onToggleFullScreen: (isFullScreen) async { onToggleFullScreen: (isFullScreen) async {
if (isFullScreen) {
await MyDevice.setAllOrientations();
} else {
await MyDevice.setPortrait();
}
await MyDevice.setLandscape();
}, },
)
: const Center(
child: CircularProgressIndicator(),
), ),
); );
} }

14
lib/features/download/presentation/ui/download_page.dart

@ -16,6 +16,7 @@ import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_b
import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_event.dart'; import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_event.dart';
import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_state.dart'; import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_state.dart';
import 'package:hadi_hoda_flutter/features/download/presentation/ui/widgets/download_loading_widget.dart'; import 'package:hadi_hoda_flutter/features/download/presentation/ui/widgets/download_loading_widget.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
/// Download page that supports downloading any combination of media types. /// Download page that supports downloading any combination of media types.
/// ///
@ -35,6 +36,7 @@ class _DownloadPageState extends State<DownloadPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
WakelockPlus.enable();
final bloc = context.read<DownloadBloc>(); final bloc = context.read<DownloadBloc>();
if (!bloc.isDownloading) { if (!bloc.isDownloading) {
@ -46,6 +48,12 @@ class _DownloadPageState extends State<DownloadPage> {
} }
} }
@override
void dispose() {
WakelockPlus.disable();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -74,7 +82,7 @@ class _DownloadPageState extends State<DownloadPage> {
if (widget.config.redirectTo == Routes.homePage) { if (widget.config.redirectTo == Routes.homePage) {
context.goNamed(Routes.homePage); context.goNamed(Routes.homePage);
} else { } else {
context.pushNamed(
context.pushReplacementNamed(
widget.config.redirectTo, widget.config.redirectTo,
pathParameters: widget.config.routeParams, pathParameters: widget.config.routeParams,
); );
@ -130,6 +138,8 @@ class _DownloadPageState extends State<DownloadPage> {
Widget _text(BuildContext context) { Widget _text(BuildContext context) {
return PositionedDirectional( return PositionedDirectional(
bottom: 130, bottom: 130,
start: 0,
end: 0,
child: Column( child: Column(
spacing: MySpaces.s6, spacing: MySpaces.s6,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
@ -149,7 +159,7 @@ class _DownloadPageState extends State<DownloadPage> {
.loadingStream, .loadingStream,
builder: (context, snapshot) => builder: (context, snapshot) =>
Text( Text(
'Downloading ...${snapshot.data?.downloadedLevels}/${widget
'${context.translate.downloading} ...${snapshot.data?.downloadedLevels}/${widget
.config.downloadToLevel}', .config.downloadToLevel}',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: MYTextStyle.matn3, style: MYTextStyle.matn3,

13
lib/features/guider/presentation/ui/guider_page.dart

@ -1,5 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gif/gif.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_animations.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_audios.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_audios.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart';
@ -14,7 +16,6 @@ import 'package:hadi_hoda_flutter/core/widgets/animations/slide_anim.dart';
import 'package:hadi_hoda_flutter/core/widgets/animations/slide_down_fade.dart'; import 'package:hadi_hoda_flutter/core/widgets/animations/slide_down_fade.dart';
import 'package:hadi_hoda_flutter/core/widgets/animations/slide_up_fade.dart'; import 'package:hadi_hoda_flutter/core/widgets/animations/slide_up_fade.dart';
import 'package:hadi_hoda_flutter/core/widgets/answer_box/answer_box_showcase.dart'; import 'package:hadi_hoda_flutter/core/widgets/answer_box/answer_box_showcase.dart';
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart';
import 'package:hadi_hoda_flutter/core/widgets/pop_scope/my_pop_scope.dart'; import 'package:hadi_hoda_flutter/core/widgets/pop_scope/my_pop_scope.dart';
import 'package:hadi_hoda_flutter/core/widgets/showcase/my_showcase_widget.dart'; import 'package:hadi_hoda_flutter/core/widgets/showcase/my_showcase_widget.dart';
import 'package:hadi_hoda_flutter/features/guider/presentation/bloc/guider_bloc.dart'; import 'package:hadi_hoda_flutter/features/guider/presentation/bloc/guider_bloc.dart';
@ -223,10 +224,12 @@ class _GuiderPageState extends State<GuiderPage> with TickerProviderStateMixin {
.showCaseKey['guide_key']!, .showCaseKey['guide_key']!,
description: context.translate.showcase_guide, description: context.translate.showcase_guide,
type: ShowcaseTooltipType.top, type: ShowcaseTooltipType.top,
child: MyImage(
image: MyAssets.globe,
fit: BoxFit.cover,
size: setSize(context: context, tablet: 120),
child: Gif(
image: const AssetImage(MyAnimations.globeStateNormal),
fps: 10,
autostart: Autostart.loop,
width: setSize(context: context, mobile: 80, tablet: 120) ?? 80,
height: setSize(context: context, mobile: 80, tablet: 120) ?? 80,
), ),
), ),
PositionedDirectional( PositionedDirectional(

107
lib/features/home/presentation/ui/home_page.dart

@ -9,14 +9,14 @@ import 'package:hadi_hoda_flutter/core/utils/local_storage.dart';
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; import 'package:hadi_hoda_flutter/core/utils/my_localization.dart';
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; import 'package:hadi_hoda_flutter/core/utils/screen_size.dart';
import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart'; import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart';
import 'package:hadi_hoda_flutter/core/widgets/animations/slide_down_fade.dart';
import 'package:hadi_hoda_flutter/core/widgets/animations/slide_up_fade.dart';
import 'package:hadi_hoda_flutter/core/widgets/button/my_white_button.dart';
import 'package:hadi_hoda_flutter/core/widgets/button/my_yellow_button.dart'; import 'package:hadi_hoda_flutter/core/widgets/button/my_yellow_button.dart';
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart';
import 'package:hadi_hoda_flutter/core/widgets/inkwell/my_inkwell.dart'; import 'package:hadi_hoda_flutter/core/widgets/inkwell/my_inkwell.dart';
import 'package:hadi_hoda_flutter/core/widgets/pop_scope/my_pop_scope.dart'; import 'package:hadi_hoda_flutter/core/widgets/pop_scope/my_pop_scope.dart';
import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_bloc.dart'; import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_bloc.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/ui/widgets/payment_dialog.dart';
class HomePage extends StatefulWidget { class HomePage extends StatefulWidget {
const HomePage({super.key}); const HomePage({super.key});
@ -26,6 +26,8 @@ class HomePage extends StatefulWidget {
} }
class _HomePageState extends State<HomePage> { class _HomePageState extends State<HomePage> {
static const double _muteThreshold = 0.001;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -51,6 +53,10 @@ class _HomePageState extends State<HomePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final lang =
LocalStorage.readData(key: MyConstants.selectLanguage) ??
MyConstants.defaultLanguage;
late final isArabic = lang == 'ar';
return Scaffold( return Scaffold(
body: MyPopScope( body: MyPopScope(
child: DecoratedBox( child: DecoratedBox(
@ -64,9 +70,9 @@ class _HomePageState extends State<HomePage> {
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
_music(context),
_topBtns(context, isArabic),
_image(context), _image(context),
_bottomBtns(context),
_bottomBtns(context, isArabic),
], ],
), ),
), ),
@ -76,23 +82,18 @@ class _HomePageState extends State<HomePage> {
} }
Widget _music(BuildContext context) { Widget _music(BuildContext context) {
return PositionedDirectional(
top: setPlatform(android: MySpaces.s36, iOS: 50),
end: MySpaces.s16,
child: SlideDownFade(
delay: const Duration(milliseconds: 100),
child: StreamBuilder<double>(
return StreamBuilder<double>(
initialData: 1, initialData: 1,
stream: context.read<HomeBloc>().volumeStream, stream: context.read<HomeBloc>().volumeStream,
builder: (context, snapshot) => MyInkwell( builder: (context, snapshot) => MyInkwell(
onTap: () => context.read<HomeBloc>().changeMute(), onTap: () => context.read<HomeBloc>().changeMute(),
child: MyImage( child: MyImage(
image: snapshot.data == 0 ? MyAssets.musicOff : MyAssets.musicOn,
image: (snapshot.data ?? 1) <= _muteThreshold
? MyAssets.musicOff
: MyAssets.musicOn,
size: setSize(context: context, tablet: 100), size: setSize(context: context, tablet: 100),
), ),
), ),
),
),
); );
} }
@ -112,27 +113,94 @@ class _HomePageState extends State<HomePage> {
); );
} }
Positioned _bottomBtns(BuildContext context) {
Positioned _topBtns(BuildContext context, bool isArabic) {
return Positioned(
top: setPlatform(android: MySpaces.s36, iOS: 50),
right: 20,
left: 20,
child: !isArabic
? Align(
alignment: AlignmentDirectional.topStart,
child: _music(context),
)
: Row(
children: [
MyInkwell(
onTap: () => context.read<HomeBloc>().showAboutUs(context),
child: MyImage(
image: MyAssets.theme,
size: setSize(context: context, tablet: 100),
),
),
const SizedBox(width: 12),
MyInkwell(
onTap: () =>
context.read<HomeBloc>().goToLanguagePage(context),
child: MyImage(
image: MyAssets.language,
size: setSize(context: context, tablet: 100),
),
),
const Spacer(),
_music(context),
],
),
);
}
Positioned _bottomBtns(BuildContext context, bool isArabic) {
return Positioned( return Positioned(
bottom: setPlatform(android: MySpaces.s40, iOS: MySpaces.s0), bottom: setPlatform(android: MySpaces.s40, iOS: MySpaces.s0),
left: MySpaces.s16, left: MySpaces.s16,
right: MySpaces.s16, right: MySpaces.s16,
child: SafeArea( child: SafeArea(
child: SlideUpFade(
delay: const Duration(milliseconds: 100),
child: Row(
child: isArabic
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
flex: 4,
child: MyYellowButton(
onTap: () {
context.read<HomeBloc>().goToLevelPage(context);
},
title: context.translate.start,
),
),
const SizedBox(width: 20),
Expanded(
flex: 3,
child: MyWhiteButton(
title: 'تبرع',
onTap: () {
showDialog(
context: context,
builder: (_) => const PaymentDialog(),
);
},
bgSvgMobile: MyAssets.button35,
),
),
],
)
: Row(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
MyInkwell( MyInkwell(
onTap: () => context.read<HomeBloc>().goToLanguagePage(context),
onTap: () =>
context.read<HomeBloc>().goToLanguagePage(context),
child: MyImage( child: MyImage(
image: MyAssets.language, image: MyAssets.language,
size: setSize(context: context, tablet: 100), size: setSize(context: context, tablet: 100),
), ),
), ),
MyYellowButton( MyYellowButton(
onTap: () => context.read<HomeBloc>().goToLevelPage(context),
onTap: () {
context.read<HomeBloc>().goToLevelPage(context);
},
title: context.translate.start, title: context.translate.start,
), ),
MyInkwell( MyInkwell(
@ -145,7 +213,6 @@ class _HomePageState extends State<HomePage> {
], ],
), ),
), ),
),
); );
} }
} }

75
lib/features/home/presentation/ui/widgets/payment_dialog.dart

@ -0,0 +1,75 @@
import 'package:flutter/material.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
import 'package:url_launcher/url_launcher.dart';
class PaymentDialog extends StatelessWidget {
const PaymentDialog({super.key});
static const String _paymentUrl =
'https://buy.stripe.com/cNi6oI9EF5Y44oQ5nb4AU0V';
Future<void> _launchPayment() async {
final Uri url = Uri.parse(_paymentUrl);
if (await canLaunchUrl(url)) {
await launchUrl(url);
}
}
@override
Widget build(BuildContext context) {
return Dialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
const Text(
"ساهم معنا في تطوير مشاريعنا القادمة",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
fontFamily: MYTextStyle.notoSansArabic,
),
),
const SizedBox(height: 16),
const Text(
'''قال رسول الله (صلى الله عليه وآله): "إذا مات ابن آدم انقطع عمله إلا من ثلاث: صدقة جارية، أو علم ينتفع به، أو ولد صالح يدعو له".
إن دعمكم المستمر لتطوير مشاريعنا القادمة هو التجسيد الحقيقي لـ «الباقيات الصالحات».
تخيل أن كل طفلٍ في أي بقعةٍ من العالم، وبأي لسانٍ كان، يتعلم الصلاة أو يكتسب أدباً من آداب الدين بفضل مساهمتكم؛ سيُكتب ذلك في ميزان حسناتكم «علماً يُنتفع به»، وصدقةً جاريةً لا ينقطع ثوابها أبدَ الآبدين.''',
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 15,
height: 1.6,
fontFamily: MYTextStyle.notoSansArabic,
),
),
const SizedBox(height: 24),
SizedBox(
width: double.infinity,
child: FilledButton(
onPressed: _launchPayment,
child: const Text(
"تبرع الآن",
style: TextStyle(
fontFamily: MYTextStyle.notoSansArabic,
fontSize: 17,
fontWeight: FontWeight.w600,
),
),
),
),
],
),
),
);
}
}

8
lib/features/language/data/repository_impl/language_repository_impl.dart

@ -13,12 +13,18 @@ class LanguageRepositoryImpl implements ILanguageRepository {
LanguageRepositoryImpl(this.languageDatasource); LanguageRepositoryImpl(this.languageDatasource);
List<LanguageEntity>? _langs;
@override @override
Future<DataState<List<LanguageEntity>, MyException>> getLanguages() async { Future<DataState<List<LanguageEntity>, MyException>> getLanguages() async {
try { try {
if(_langs != null) {
return DataState.success(_langs!);
}
final languages = await languageDatasource.getLanguages(); final languages = await languageDatasource.getLanguages();
// Explicitly convert to a List<LanguageEntity> to prevent runtime type errors. // Explicitly convert to a List<LanguageEntity> to prevent runtime type errors.
return DataState.success(List<LanguageEntity>.from(languages));
_langs = List<LanguageEntity>.from(languages);
return DataState.success(_langs!);
} on MyException catch (e) { } on MyException catch (e) {
return DataState.error(e); return DataState.error(e);
} }

137
lib/features/language/presentation/ui/language_page.dart

@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.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/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/constants/my_constants.dart';
@ -13,6 +12,7 @@ import 'package:hadi_hoda_flutter/core/utils/my_localization.dart';
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; import 'package:hadi_hoda_flutter/core/utils/screen_size.dart';
import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart'; import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart';
import 'package:hadi_hoda_flutter/core/widgets/button/my_blue_button.dart'; import 'package:hadi_hoda_flutter/core/widgets/button/my_blue_button.dart';
import 'package:hadi_hoda_flutter/core/widgets/error/error_state.dart';
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart';
import 'package:hadi_hoda_flutter/features/app/presentation/bloc/app_bloc.dart'; import 'package:hadi_hoda_flutter/features/app/presentation/bloc/app_bloc.dart';
import 'package:hadi_hoda_flutter/features/app/presentation/bloc/app_event.dart'; import 'package:hadi_hoda_flutter/features/app/presentation/bloc/app_event.dart';
@ -24,6 +24,9 @@ import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_e
import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_state.dart'; import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_state.dart';
import 'package:wheel_chooser/wheel_chooser.dart'; import 'package:wheel_chooser/wheel_chooser.dart';
import '../../../../core/widgets/animations/rotation_anim.dart';
import '../../domain/entity/language_entity.dart';
class LanguagePage extends StatefulWidget { class LanguagePage extends StatefulWidget {
const LanguagePage({super.key}); const LanguagePage({super.key});
@ -34,10 +37,18 @@ class LanguagePage extends StatefulWidget {
class _LanguagePageState extends State<LanguagePage> { class _LanguagePageState extends State<LanguagePage> {
final controller = FixedExtentScrollController(initialItem: 50); final controller = FixedExtentScrollController(initialItem: 50);
bool _isEnabled = false; bool _isEnabled = false;
LanguageEntity? _selectedLanguage;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_selectedLanguage =
context.read<LanguageBloc>().state.selectedLanguage ??
const LanguageEntity(
code: 'en',
title: '',
displayName: 'English (English',
);
context.read<LanguageBloc>().add(const GetLanguagesEvent()); context.read<LanguageBloc>().add(const GetLanguagesEvent());
} }
@ -74,7 +85,9 @@ class _LanguagePageState extends State<LanguagePage> {
if (state.languages.isNotEmpty && !_isEnabled) { if (state.languages.isNotEmpty && !_isEnabled) {
int targetIndex = 0; int targetIndex = 0;
if (state.selectedLanguage != null) { if (state.selectedLanguage != null) {
targetIndex = state.languages.indexWhere((l) => l.code == state.selectedLanguage!.code);
targetIndex = state.languages.indexWhere(
(l) => l.code == state.selectedLanguage!.code,
);
if (targetIndex == -1) targetIndex = 0; if (targetIndex == -1) targetIndex = 0;
} }
@ -97,17 +110,41 @@ class _LanguagePageState extends State<LanguagePage> {
}, },
builder: (context, state) { builder: (context, state) {
if (state.getLanguagesStatus is BaseLoading) { if (state.getLanguagesStatus is BaseLoading) {
return const Center(child: CircularProgressIndicator());
return Center(child: _loading(context));
}
if (state.getLanguagesStatus is BaseError) {
return Padding(
padding: EdgeInsets.symmetric(
vertical:
MediaQuery.viewPaddingOf(context).bottom + MySpaces.s16,
horizontal: 60,
),
child: ErrorState(
onTap: () {
context.read<LanguageBloc>().add(const GetLanguagesEvent());
},
),
);
} }
final double itemSize = setSize(context: context, mobile: 45, tablet: 60) ?? 45;
final double itemSize =
setSize(context: context, mobile: 45, tablet: 60) ?? 45;
final selectStyle = MYTextStyle.titr1.copyWith(
// Keep same size as before (WheelChooser.choices)
fontSize: itemSize * 0.34,
color: Colors.white,
);
final unSelectStyle = MYTextStyle.titr1.copyWith(
fontSize: itemSize * 0.34,
color: Colors.white.withValues(alpha: 0.5),
);
return Padding( return Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: setSize(context: context, mobile: 50, tablet: 0.3.w) ?? 0, left: setSize(context: context, mobile: 50, tablet: 0.3.w) ?? 0,
right: setSize(context: context, mobile: 50, tablet: 0.3.w) ?? 0,
right:
setSize(context: context, mobile: 50, tablet: 0.3.w) ?? 0,
bottom: MySpaces.s40 + MediaQuery.paddingOf(context).bottom, bottom: MySpaces.s40 + MediaQuery.paddingOf(context).bottom,
top: 100,
top: 60,
), ),
child: Column( child: Column(
children: [ children: [
@ -135,41 +172,75 @@ class _LanguagePageState extends State<LanguagePage> {
width: double.infinity, width: double.infinity,
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
height: itemSize, height: itemSize,
padding: const EdgeInsets.symmetric(horizontal: 18),
padding: const EdgeInsets.symmetric(
horizontal: 18,
),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white.withValues(alpha: .2), color: Colors.white.withValues(alpha: .2),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Transform.translate(
offset:
(state
.selectedLanguage
?.displayName
.length ??
0) >
21
? const Offset(-10, 0)
: Offset.zero,
child: const MyImage(image: MyAssets.check), child: const MyImage(image: MyAssets.check),
), ),
), ),
WheelChooser.choices(
),
WheelChooser.custom(
controller: controller, controller: controller,
datas: null,
startPosition: null, startPosition: null,
perspective: 0.00000001, perspective: 0.00000001,
listWidth: double.infinity, listWidth: double.infinity,
listHeight: double.infinity, listHeight: double.infinity,
itemSize: itemSize, itemSize: itemSize,
isInfinite: true, isInfinite: true,
onChoiceChanged: (value) {
if (value != null && value is! String) {
context.read<LanguageBloc>().add(
SelectLanguageEvent(value),
);
}
onValueChanged: (position) {
if (state.languages.isEmpty) return;
final pos = position is int
? position
: int.tryParse(position.toString()) ?? 0;
final index =
((pos % state.languages.length) +
state.languages.length) %
state.languages.length;
final lang = state.languages[index];
setState(() {
_selectedLanguage = lang;
});
}, },
selectTextStyle: MYTextStyle.titr1.copyWith(
fontSize: itemSize * 0.34,
color: Colors.white,
children: state.languages.map((lang) {
final isSelected =
_selectedLanguage?.code == lang.code;
return Center(
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 36,
),
child: MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler: const TextScaler.linear(1.5),
),
child: AutoSizeText(
lang.displayName,
textAlign: TextAlign.center,
maxLines: 2,
minFontSize: 10,
stepGranularity: 0.5,
overflow: TextOverflow.ellipsis,
style: isSelected
? selectStyle
: unSelectStyle,
),
), ),
unSelectTextStyle: MYTextStyle.titr1.copyWith(
fontSize: itemSize * 0.34,
color: Colors.white.withOpacity(0.5),
), ),
choices: state.languages.map((lang) {
return WheelChoice(
value: lang,
title: lang.displayName,
); );
}).toList(), }).toList(),
), ),
@ -188,6 +259,15 @@ class _LanguagePageState extends State<LanguagePage> {
); );
} }
Widget _loading(BuildContext context) {
return RotationAnim(
child: MyImage(
image: MyAssets.loading,
size: setSize(context: context, mobile: 110, tablet: 145),
),
);
}
Widget _title(BuildContext context) { Widget _title(BuildContext context) {
return Row( return Row(
spacing: MySpaces.s10, spacing: MySpaces.s10,
@ -208,15 +288,18 @@ class _LanguagePageState extends State<LanguagePage> {
Widget _btn(BuildContext context, LanguageState state) { Widget _btn(BuildContext context, LanguageState state) {
return MyBlueButton( return MyBlueButton(
onTap: (_isEnabled && state.selectedLanguage != null)
onTap: (_isEnabled && _selectedLanguage != null)
? () async { ? () async {
context.read<LanguageBloc>().add(
SelectLanguageEvent(_selectedLanguage!),
);
final downloadBloc = context.read<DownloadBloc>(); final downloadBloc = context.read<DownloadBloc>();
// 1. Cancel any previous downloads. // 1. Cancel any previous downloads.
downloadBloc.add(CancelDownloadEvent()); downloadBloc.add(CancelDownloadEvent());
// 2. Update App Locale // 2. Update App Locale
context.read<AppBloc>().add( context.read<AppBloc>().add(
ChangeLocaleEvent(state.selectedLanguage!.locale),
ChangeLocaleEvent(_selectedLanguage!.locale),
); );
await Future.delayed(const Duration(milliseconds: 80)); await Future.delayed(const Duration(milliseconds: 80));
final lastDownloadedLevel = await downloadBloc final lastDownloadedLevel = await downloadBloc

58
lib/features/level/data/datasource/level_datasource.dart

@ -1,8 +1,12 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:hadi_hoda_flutter/core/constants/my_api.dart';
import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/constants/my_constants.dart';
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart';
import 'package:hadi_hoda_flutter/core/network/http_request.dart';
import 'package:hadi_hoda_flutter/core/params/level_params.dart'; import 'package:hadi_hoda_flutter/core/params/level_params.dart';
import 'package:hadi_hoda_flutter/core/response/base_response.dart';
import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; import 'package:hadi_hoda_flutter/core/utils/local_storage.dart';
import 'package:hadi_hoda_flutter/features/level/data/model/node_model.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/node_entity.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/node_entity.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/total_data_entity.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/total_data_entity.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
@ -13,21 +17,63 @@ abstract class ILevelDatasource {
/// Local /// Local
class LocalLevelDatasourceImpl implements ILevelDatasource { class LocalLevelDatasourceImpl implements ILevelDatasource {
const LocalLevelDatasourceImpl();
final IHttpRequest httpRequest;
LocalLevelDatasourceImpl(this.httpRequest);
String get _selectedLanguage =>
LocalStorage.readData(key: MyConstants.selectLanguage) ??
MyConstants.defaultLanguage;
Future<void> _syncRemoteLevelsToLocal() async {
final response = await httpRequest.get(
path: MyApi.levels,
queryParameters: {'lang': _selectedLanguage},
);
final List<NodeEntity> levels = BaseResponse.getDataList<NodeEntity>(
response?['path'],
(json) => NodeModel.fromJson(json),
);
await LocalStorage.saveData(
key: MyConstants.maxLevelCount,
value: '${levels.length}',
);
final Box<TotalDataEntity> levelBox = Hive.box(MyConstants.levelBox);
dynamic existingKey;
for (final key in levelBox.keys) {
final item = levelBox.get(key);
if (item?.code == _selectedLanguage) {
existingKey = key;
break;
}
}
if (existingKey != null) {
await levelBox.put(
existingKey,
TotalDataEntity(code: _selectedLanguage, nodes: levels),
);
return;
}
await levelBox.add(TotalDataEntity(code: _selectedLanguage, nodes: levels));
}
@override @override
Future<List<NodeEntity>> getLevels({required LevelParams params}) async { Future<List<NodeEntity>> getLevels({required LevelParams params}) async {
try { try {
final String selectedLanguage = LocalStorage.readData(
key: MyConstants.selectLanguage) ?? MyConstants.defaultLanguage;
if (params.forceRemote) {
await _syncRemoteLevelsToLocal();
}
final Box<TotalDataEntity> levelBox = Hive.box(MyConstants.levelBox); final Box<TotalDataEntity> levelBox = Hive.box(MyConstants.levelBox);
final TotalDataEntity findData = levelBox.values.firstWhere( final TotalDataEntity findData = levelBox.values.firstWhere(
(e) => e.code == selectedLanguage,
(e) => e.code == _selectedLanguage,
orElse: () => TotalDataEntity(), orElse: () => TotalDataEntity(),
); );
debugPrint("nodesCount : ${findData.nodes?.first.level?.questions}");
return findData.nodes ?? []; return findData.nodes ?? [];
} catch (e,s) {
} catch (e, s) {
debugPrint(e.toString()); debugPrint(e.toString());
debugPrint(s.toString()); debugPrint(s.toString());
throw const MyException(errorMessage: 'Operation Failed'); throw const MyException(errorMessage: 'Operation Failed');

5
lib/features/level/domain/entity/node_entity.dart

@ -9,11 +9,14 @@ enum NodeType {
@HiveField(0) @HiveField(0)
level, level,
@HiveField(1) @HiveField(1)
prize;
prize,
@HiveField(2)
comingSoon;
static Map<String, NodeType> get fromJson => { static Map<String, NodeType> get fromJson => {
'level': NodeType.level, 'level': NodeType.level,
'prize': NodeType.prize, 'prize': NodeType.prize,
'coming_soon': NodeType.comingSoon,
}; };
} }

5
lib/features/level/domain/entity/node_entity.g.dart

@ -57,6 +57,8 @@ class NodeTypeAdapter extends TypeAdapter<NodeType> {
return NodeType.level; return NodeType.level;
case 1: case 1:
return NodeType.prize; return NodeType.prize;
case 2:
return NodeType.comingSoon;
default: default:
return NodeType.level; return NodeType.level;
} }
@ -71,6 +73,9 @@ class NodeTypeAdapter extends TypeAdapter<NodeType> {
case NodeType.prize: case NodeType.prize:
writer.writeByte(1); writer.writeByte(1);
break; break;
case NodeType.comingSoon:
writer.writeByte(2);
break;
} }
} }

52
lib/features/level/presentation/bloc/level_bloc.dart

@ -56,7 +56,7 @@ class LevelBloc extends Bloc<LevelEvent, LevelState> {
final GetLastDownloadedLevel _getLastDownloadedLevel; final GetLastDownloadedLevel _getLastDownloadedLevel;
/// ------------Variables------------ /// ------------Variables------------
final List<LevelLocation> locationList = [
List<LevelLocation> locationList = [
LevelLocation( LevelLocation(
bottom: setSize(context: MyContext.get, mobile: -0.03.h, tablet: -0.03.h), bottom: setSize(context: MyContext.get, mobile: -0.03.h, tablet: -0.03.h),
left: setSize(context: MyContext.get, mobile: 0.1.w, tablet: 0.2.w), left: setSize(context: MyContext.get, mobile: 0.1.w, tablet: 0.2.w),
@ -64,7 +64,7 @@ class LevelBloc extends Bloc<LevelEvent, LevelState> {
), ),
LevelLocation( LevelLocation(
bottom: setSize(context: MyContext.get, mobile: 0.03.h, tablet: 0.1.h), bottom: setSize(context: MyContext.get, mobile: 0.03.h, tablet: 0.1.h),
left: setSize(context: MyContext.get, mobile: 0.28.w, tablet: 0.4.w),
left: setSize(context: MyContext.get, mobile: 0.28.w, tablet: 0.38.w),
index: 1, index: 1,
), ),
LevelLocation( LevelLocation(
@ -73,48 +73,48 @@ class LevelBloc extends Bloc<LevelEvent, LevelState> {
index: 2, index: 2,
), ),
LevelLocation( LevelLocation(
bottom: setSize(context: MyContext.get, mobile: 0.2.h, tablet: 0.38.h),
bottom: setSize(context: MyContext.get, mobile: 0.2.h, tablet: 0.33.h),
left: setSize(context: MyContext.get, mobile: 0.3.w, tablet: 0.3.w), left: setSize(context: MyContext.get, mobile: 0.3.w, tablet: 0.3.w),
index: 3, index: 3,
), ),
LevelLocation( LevelLocation(
bottom: setSize(context: MyContext.get, mobile: 0.24.h, tablet: 0.47.h),
left: setSize(context: MyContext.get, mobile: 0.5.w, tablet: 0.5.w),
bottom: setSize(context: MyContext.get, mobile: 0.24.h, tablet: 0.4.h),
left: setSize(context: MyContext.get, mobile: 0.5.w, tablet: 0.55.w),
index: 4, index: 4,
), ),
LevelLocation( LevelLocation(
bottom: setSize(context: MyContext.get, mobile: 0.33.h, tablet: 0.55.h), bottom: setSize(context: MyContext.get, mobile: 0.33.h, tablet: 0.55.h),
left: setSize(context: MyContext.get, mobile: 0.63.w, tablet: 0.8.w),
left: setSize(context: MyContext.get, mobile: 0.659.w, tablet: 0.77.w),
index: 5, index: 5,
), ),
LevelLocation( LevelLocation(
bottom: setSize(context: MyContext.get, mobile: 0.45.h, tablet: 0.8.h), bottom: setSize(context: MyContext.get, mobile: 0.45.h, tablet: 0.8.h),
left: setSize(context: MyContext.get, mobile: 0.57.w, tablet: 0.8.w),
left: setSize(context: MyContext.get, mobile: 0.59.w, tablet: 0.68.w),
index: 6, index: 6,
), ),
LevelLocation( LevelLocation(
bottom: setSize(context: MyContext.get, mobile: 0.55.h, tablet: 1.1.h),
left: setSize(context: MyContext.get, mobile: 0.57.w, tablet: 0.77.w),
bottom: setSize(context: MyContext.get, mobile: 0.55.h, tablet: .951.h),
left: setSize(context: MyContext.get, mobile: 0.584.w, tablet: 0.64.w),
index: 7, index: 7,
), ),
LevelLocation( LevelLocation(
bottom: setSize(context: MyContext.get, mobile: 0.6.h, tablet: 1.2.h),
left: setSize(context: MyContext.get, mobile: 0.4.w, tablet: 0.65.w),
bottom: setSize(context: MyContext.get, mobile: 0.61.h, tablet: 1.03.h),
left: setSize(context: MyContext.get, mobile: 0.4.w, tablet: 0.18.w),
index: 8, index: 8,
), ),
LevelLocation( LevelLocation(
bottom: setSize(context: MyContext.get, mobile: 0.63.h, tablet: 1.25.h),
left: setSize(context: MyContext.get, mobile: 0.2.w, tablet: 0.3.w),
bottom: setSize(context: MyContext.get, mobile: 0.64.h, tablet: 1.19.h),
left: setSize(context: MyContext.get, mobile: 0.2.w, tablet: 0.22.w),
index: 9, index: 9,
), ),
LevelLocation( LevelLocation(
bottom: setSize(context: MyContext.get, mobile: 0.7.h, tablet: 1.38.h),
left: setSize(context: MyContext.get, mobile: 0, tablet: 0.04.w),
bottom: setSize(context: MyContext.get, mobile: 0.7.h, tablet: 1.25.h),
left: setSize(context: MyContext.get, mobile: 0, tablet: 0.53.w),
index: 10, index: 10,
), ),
LevelLocation( LevelLocation(
bottom: setSize(context: MyContext.get, mobile: 0.75.h, tablet: 1.46.h),
left: setSize(context: MyContext.get, mobile: 0.15.w, tablet: 0.22.w),
bottom: setSize(context: MyContext.get, mobile: 0.75.h, tablet: 1.39.h),
left: setSize(context: MyContext.get, mobile: 0.15.w, tablet: 0.58.w),
index: 11, index: 11,
), ),
LevelLocation( LevelLocation(
@ -287,9 +287,11 @@ class LevelBloc extends Bloc<LevelEvent, LevelState> {
final int currentLevel = int.parse( final int currentLevel = int.parse(
LocalStorage.readData(key: MyConstants.currentLevel) ?? '1', LocalStorage.readData(key: MyConstants.currentLevel) ?? '1',
); );
await _getLeveslUseCase(LevelParams()).then((value) {
value.fold((data) async {
nodeList.addAll(data);
final localResult = await _getLeveslUseCase(LevelParams());
localResult.fold((data) async {
nodeList
..clear()
..addAll(data);
try { try {
emit( emit(
state.copyWith( state.copyWith(
@ -309,7 +311,15 @@ class LevelBloc extends Bloc<LevelEvent, LevelState> {
} }
add(StartScrollEvent()); add(StartScrollEvent());
}, (error) {}); }, (error) {});
});
if (nodeList.isNotEmpty && nodeList.last.nodeType == NodeType.comingSoon) {
final remoteResult = await _getLeveslUseCase(LevelParams(forceRemote: true));
remoteResult.fold((data) {
nodeList
..clear()
..addAll(data);
}, (error) {});
}
} }
FutureOr<void> _startScrollEvent( FutureOr<void> _startScrollEvent(

37
lib/features/level/presentation/ui/level_page.dart

@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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_assets.dart';
@ -13,15 +14,14 @@ import 'package:hadi_hoda_flutter/core/widgets/animations/ship_anim.dart';
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart';
import 'package:hadi_hoda_flutter/core/widgets/inkwell/my_inkwell.dart'; import 'package:hadi_hoda_flutter/core/widgets/inkwell/my_inkwell.dart';
import 'package:hadi_hoda_flutter/core/widgets/pop_scope/my_pop_scope.dart'; import 'package:hadi_hoda_flutter/core/widgets/pop_scope/my_pop_scope.dart';
import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart';
import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_bloc.dart'; import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_bloc.dart';
import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_event.dart'; import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_event.dart';
import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_state.dart';
import 'package:hadi_hoda_flutter/features/download/presentation/ui/widgets/download_loading_widget.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/node_entity.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_bloc.dart'; import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_bloc.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_event.dart'; import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_event.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_state.dart'; import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_state.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/coming_soon_level.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/diamond_level.dart'; import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/diamond_level.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/level_path.dart'; import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/level_path.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/node_widget.dart'; import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/node_widget.dart';
@ -35,10 +35,12 @@ class LevelPage extends StatefulWidget {
} }
class _LevelPageState extends State<LevelPage> { class _LevelPageState extends State<LevelPage> {
static const double _muteThreshold = 0.001;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_triggerRemainingLevelsDownload();
if(!kDebugMode)_triggerRemainingLevelsDownload();
} }
void _triggerRemainingLevelsDownload() { void _triggerRemainingLevelsDownload() {
@ -64,8 +66,9 @@ class _LevelPageState extends State<LevelPage> {
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
_background(context), _background(context),
_path(context),
_planets(context), _planets(context),
_path(context),
], ],
), ),
), ),
@ -214,8 +217,8 @@ class _LevelPageState extends State<LevelPage> {
return Positioned.fill( return Positioned.fill(
top: 250, top: 250,
bottom: 150, bottom: 150,
right: 60,
left: 60,
right: 50,
left: 50,
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
@ -235,7 +238,18 @@ class _LevelPageState extends State<LevelPage> {
children: [ children: [
...List.generate( ...List.generate(
context.read<LevelBloc>().nodeList.length, context.read<LevelBloc>().nodeList.length,
(index) => Positioned(
(index) {
final node = context.read<LevelBloc>().nodeList[index];
if(node.nodeType == NodeType.comingSoon) {
if(node.nodeType == NodeType.comingSoon) {
return Positioned(
left: 0,
right: 0,
bottom: (context.read<LevelBloc>().locationList[index].bottom ?? 0) + 88,
child: const ComingSoonLevel());
}
}
return Positioned(
top: context.read<LevelBloc>().locationList[index].top, top: context.read<LevelBloc>().locationList[index].top,
bottom: context.read<LevelBloc>().locationList[index].bottom, bottom: context.read<LevelBloc>().locationList[index].bottom,
right: context.read<LevelBloc>().locationList[index].right, right: context.read<LevelBloc>().locationList[index].right,
@ -261,7 +275,8 @@ class _LevelPageState extends State<LevelPage> {
}, },
), ),
), ),
),
);
},
), ),
], ],
), ),
@ -316,7 +331,9 @@ class _LevelPageState extends State<LevelPage> {
builder: (context, snapshot) => MyInkwell( builder: (context, snapshot) => MyInkwell(
onTap: () => context.read<LevelBloc>().changeMute(), onTap: () => context.read<LevelBloc>().changeMute(),
child: MyImage( child: MyImage(
image: snapshot.data == 0 ? MyAssets.musicOff : MyAssets.musicOn,
image: (snapshot.data ?? 1) <= _muteThreshold
? MyAssets.musicOff
: MyAssets.musicOn,
size: setSize(context: context, tablet: 80), size: setSize(context: context, tablet: 80),
), ),
), ),

41
lib/features/level/presentation/ui/widgets/coming_soon_level.dart

@ -0,0 +1,41 @@
import 'package:flutter/material.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
import '../../../../../common_ui/resources/my_assets.dart';
import '../../../../../core/utils/my_localization.dart';
import '../../../../../core/widgets/images/my_image.dart';
class ComingSoonLevel extends StatelessWidget {
const ComingSoonLevel({super.key});
@override
Widget build(BuildContext context) {
return Stack(
clipBehavior: Clip.none,
children: [
Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 42,
).copyWith(bottom: 32),
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
gradient: LinearGradient(
colors: [
const Color(0xff7E6C81).withValues(alpha: .9),
const Color(0xff48333F).withValues(alpha: .9),
],
),
),
child: Text(context.translate.coming_soon, style: MYTextStyle.titr4, textAlign: TextAlign.center,),
),
const Positioned(
left: 0,
right: 0,
top: -58, child: MyImage(image: MyAssets.lock)),
],
);
}
}

2
lib/features/level/presentation/ui/widgets/level_path.dart

@ -6,7 +6,7 @@ class LevelPath extends StatelessWidget {
super.key, super.key,
}); });
final double width = 357;
final double width = 377;
final double height = 2230; final double height = 2230;
@override @override

44
lib/features/level/presentation/ui/widgets/node_widget.dart

@ -9,6 +9,8 @@ import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart
import 'package:hadi_hoda_flutter/features/level/domain/entity/node_entity.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/node_entity.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/prize_entity.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/prize_entity.dart';
import 'coming_soon_level.dart';
enum LevelType { enum LevelType {
unFinished, unFinished,
finished, finished,
@ -55,7 +57,9 @@ class NodeWidget extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Builder( return Builder(
builder: (context) { builder: (context) {
if (node.nodeType == NodeType.prize) {
if (node.nodeType == NodeType.comingSoon) {
return const ComingSoonLevel();
} else if (node.nodeType == NodeType.prize) {
return MyInkwell( return MyInkwell(
onTap: () { onTap: () {
if (getReward(node.prize?.afterLevel ?? 1)) { if (getReward(node.prize?.afterLevel ?? 1)) {
@ -75,11 +79,16 @@ class NodeWidget extends StatelessWidget {
), ),
); );
} else { } else {
return InkWell(
return Transform.translate(
offset: const Offset(-8, 8),
child: InkWell(
onTap: () => onTap?.call( onTap: () => onTap?.call(
node.level ?? LevelEntity(), node.level ?? LevelEntity(),
type(node.level?.order ?? 1), type(node.level?.order ?? 1),
), ),
child: Container(
color: Colors.transparent,
padding: const EdgeInsets.all(8),
child: Stack( child: Stack(
alignment: Alignment.topCenter, alignment: Alignment.topCenter,
clipBehavior: Clip.none, clipBehavior: Clip.none,
@ -126,22 +135,41 @@ class NodeWidget extends StatelessWidget {
), ),
if (node.level?.id == chooseLevel?.id) if (node.level?.id == chooseLevel?.id)
Positioned( Positioned(
top: setSize(context: context, mobile: -20, tablet: -30),
top: setSize(
context: context,
mobile: -20,
tablet: -30,
),
child: MyImage( child: MyImage(
image: MyAssets.location, image: MyAssets.location,
size: setSize(context: context, mobile: 26, tablet: 40),
size: setSize(
context: context,
mobile: 26,
tablet: 40,
),
), ),
), ),
if (type(node.level?.order ?? 1) == LevelType.finished) if (type(node.level?.order ?? 1) == LevelType.finished)
Positioned( Positioned(
bottom: 0, bottom: 0,
child: Container( child: Container(
height: setSize(context: context, mobile: 17, tablet: 24),
width: setSize(context: context, mobile: 17, tablet: 24),
height: setSize(
context: context,
mobile: 17,
tablet: 24,
),
width: setSize(
context: context,
mobile: 17,
tablet: 24,
),
padding: const EdgeInsets.all(3), padding: const EdgeInsets.all(3),
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
border: Border.all(width: 1, color: const Color(0XFF3CFF3C)),
border: Border.all(
width: 1,
color: const Color(0XFF3CFF3C),
),
gradient: const LinearGradient( gradient: const LinearGradient(
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
@ -153,6 +181,8 @@ class NodeWidget extends StatelessWidget {
), ),
], ],
), ),
),
),
); );
} }
}, },

16
lib/features/level/presentation/ui/widgets/play_button.dart

@ -24,7 +24,7 @@ class PlayButton extends StatelessWidget {
children: [ children: [
const MyImage(image: MyAssets.button2), const MyImage(image: MyAssets.button2),
Positioned( Positioned(
top: MySpaces.s2,
top: MySpaces.s6,
child: Row( child: Row(
spacing: MySpaces.s4, spacing: MySpaces.s4,
children: [ children: [
@ -54,13 +54,13 @@ class PlayButton extends StatelessWidget {
], ],
), ),
), ),
Positioned(
bottom: MySpaces.s20,
child: Text(
'${context.translate.step} ${level.order ?? 0}',
style: MYTextStyle.matn3.copyWith(color: const Color(0XFFD8490B)),
),
),
// Positioned(
// bottom: MySpaces.s20,
// child: Text(
// '',
// style: MYTextStyle.matn3.copyWith(color: const Color(0XFFD8490B)),
// ),
// ),
], ],
), ),
); );

2
lib/features/question/data/model/hadith_model.dart

@ -7,6 +7,7 @@ class HadithModel extends HadithEntity {
super.narratorName, super.narratorName,
super.translation, super.translation,
super.status, super.status,
super.sourceName,
}); });
factory HadithModel.fromJson(Map<String, dynamic> json) { factory HadithModel.fromJson(Map<String, dynamic> json) {
@ -16,6 +17,7 @@ class HadithModel extends HadithEntity {
narratorName: json['narrator_name'], narratorName: json['narrator_name'],
translation: json['translation'], translation: json['translation'],
status: json['status'], status: json['status'],
sourceName: json['source_name'],
); );
} }
} }

7
lib/features/question/domain/entity/answer_entity.g.dart

@ -25,6 +25,7 @@ class AnswerEntityAdapter extends TypeAdapter<AnswerEntity> {
isActive: fields[5] as bool?, isActive: fields[5] as bool?,
audioID: fields[7] as String?, audioID: fields[7] as String?,
audioInfo: fields[8] as FileEntity?, audioInfo: fields[8] as FileEntity?,
levelOrder: fields[10] as int?,
) )
..image = fields[6] as String? ..image = fields[6] as String?
..audio = fields[9] as String?; ..audio = fields[9] as String?;
@ -33,7 +34,7 @@ class AnswerEntityAdapter extends TypeAdapter<AnswerEntity> {
@override @override
void write(BinaryWriter writer, AnswerEntity obj) { void write(BinaryWriter writer, AnswerEntity obj) {
writer writer
..writeByte(10)
..writeByte(11)
..writeByte(0) ..writeByte(0)
..write(obj.id) ..write(obj.id)
..writeByte(1) ..writeByte(1)
@ -53,7 +54,9 @@ class AnswerEntityAdapter extends TypeAdapter<AnswerEntity> {
..writeByte(8) ..writeByte(8)
..write(obj.audioInfo) ..write(obj.audioInfo)
..writeByte(9) ..writeByte(9)
..write(obj.audio);
..write(obj.audio)
..writeByte(10)
..write(obj.levelOrder);
} }
@override @override

3
lib/features/question/domain/entity/hadith_entity.dart

@ -14,6 +14,8 @@ class HadithEntity extends HiveObject {
String? translation; String? translation;
@HiveField(4) @HiveField(4)
String? status; String? status;
@HiveField(5)
String? sourceName;
HadithEntity({ HadithEntity({
this.id, this.id,
@ -21,5 +23,6 @@ class HadithEntity extends HiveObject {
this.narratorName, this.narratorName,
this.translation, this.translation,
this.status, this.status,
this.sourceName,
}); });
} }

2
lib/features/question/domain/entity/question_entity.dart

@ -65,7 +65,7 @@ class QuestionEntity extends HiveObject {
this.levelOrder, this.levelOrder,
}){ }){
audio = '${StoragePath.documentDir.path}/${LocalStorage.readData(key: MyConstants.selectLanguage)}/$levelOrder/question_audio/${audioInfo?.filename}'; audio = '${StoragePath.documentDir.path}/${LocalStorage.readData(key: MyConstants.selectLanguage)}/$levelOrder/question_audio/${audioInfo?.filename}';
correctAudio = '${StoragePath.documentDir.path}/${LocalStorage.readData(key: MyConstants.selectLanguage)}/$levelOrder/correct_answer_audio/${correctAnswerAudioInfo?.filename}';
correctAudio = '${StoragePath.documentDir.path}/${LocalStorage.readData(key: MyConstants.selectLanguage)}/$levelOrder/question_audio/${correctAnswerAudioInfo?.filename}';
image = '${StoragePath.documentDir.path}/$levelOrder/question_image/${imageInfo?.filename}'; image = '${StoragePath.documentDir.path}/$levelOrder/question_image/${imageInfo?.filename}';
} }
} }

7
lib/features/question/domain/entity/question_entity.g.dart

@ -31,6 +31,7 @@ class QuestionEntityAdapter extends TypeAdapter<QuestionEntity> {
correctAnswerAudioInfo: fields[8] as FileEntity?, correctAnswerAudioInfo: fields[8] as FileEntity?,
imageId: fields[14] as String?, imageId: fields[14] as String?,
imageInfo: fields[15] as FileEntity?, imageInfo: fields[15] as FileEntity?,
levelOrder: fields[17] as int?,
) )
..audio = fields[12] as String? ..audio = fields[12] as String?
..correctAudio = fields[13] as String? ..correctAudio = fields[13] as String?
@ -40,7 +41,7 @@ class QuestionEntityAdapter extends TypeAdapter<QuestionEntity> {
@override @override
void write(BinaryWriter writer, QuestionEntity obj) { void write(BinaryWriter writer, QuestionEntity obj) {
writer writer
..writeByte(17)
..writeByte(18)
..writeByte(0) ..writeByte(0)
..write(obj.id) ..write(obj.id)
..writeByte(1) ..writeByte(1)
@ -74,7 +75,9 @@ class QuestionEntityAdapter extends TypeAdapter<QuestionEntity> {
..writeByte(15) ..writeByte(15)
..write(obj.imageInfo) ..write(obj.imageInfo)
..writeByte(16) ..writeByte(16)
..write(obj.image);
..write(obj.image)
..writeByte(17)
..write(obj.levelOrder);
} }
@override @override

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

@ -40,7 +40,7 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
@override @override
Future<void> close() { Future<void> close() {
if (_mainAudioService.audioVolume != 0) {
if (!_mainAudioService.isMuted) {
_mainAudioService.setVolume(volume: MyConstants.musicAudioVolume); _mainAudioService.setVolume(volume: MyConstants.musicAudioVolume);
} }
_backgroundAudioService.dispose(); _backgroundAudioService.dispose();
@ -125,6 +125,11 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
await _mainAudioService.play(); await _mainAudioService.play();
} }
Future<void> playRightAnswerAudio() async {
await _mainAudioService.setAudio(assetPath: MyAudios.rightAnswer);
await _mainAudioService.play();
}
Future<void> initAudios() async { Future<void> initAudios() async {
await Future.wait([ await Future.wait([
_mainAudioService.stop(), _mainAudioService.stop(),
@ -132,7 +137,7 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
_backgroundAudioService.setAudio(assetPath: MyAudios.question), _backgroundAudioService.setAudio(assetPath: MyAudios.question),
_backgroundAudioService.setLoopMode(isLoop: true), _backgroundAudioService.setLoopMode(isLoop: true),
]); ]);
if (_mainAudioService.audioVolume != 0) {
if (!_mainAudioService.isMuted) {
await Future.wait([ await Future.wait([
_mainAudioService.setVolume(volume: MyConstants.questionAudioVolume), _mainAudioService.setVolume(volume: MyConstants.questionAudioVolume),
_backgroundAudioService.setVolume(volume: 0.1), _backgroundAudioService.setVolume(volume: 0.1),
@ -142,8 +147,11 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
} }
Future<void> playWrongAudio() async { Future<void> playWrongAudio() async {
await _effectAudioService.setAudio(assetPath: MyAudios.incorrectAnswer);
await _effectAudioService.play();
final audio = MyAudios.incorrectAnswer;
debugPrint('playWrongAudio: $audio');
await _mainAudioService.setAudio(assetPath: audio);
await _mainAudioService.play();
} }
Future<void> playAnswerAudio({String? audio}) async { Future<void> playAnswerAudio({String? audio}) async {
@ -165,13 +173,13 @@ 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 List<AnswerEntity> answers = state.currentQuestion?.answers ?? [];
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) {
await Future.delayed(const Duration(milliseconds: 500), () async {
await Future.delayed(const Duration(milliseconds: 350), () async {
if (MyContext.get.mounted) { if (MyContext.get.mounted) {
await showAnswerDialog( await showAnswerDialog(
context: MyContext.get, context: MyContext.get,
@ -191,7 +199,7 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
bool showConfetti = false, bool showConfetti = false,
bool autoClose = true, bool autoClose = true,
}) async { }) async {
if(showConfetti == false){
if (showConfetti == false) {
changeGlobeState(key: MyAnimations.globeStateSpeaking); changeGlobeState(key: MyAnimations.globeStateSpeaking);
} }
await Navigator.of(context).push( await Navigator.of(context).push(
@ -210,17 +218,19 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
} }
Future<void> getNextLevelEvent({required BuildContext context}) async { Future<void> getNextLevelEvent({required BuildContext context}) async {
await _getNextLevelUseCase(QuestionParams()).then((value) =>
value.fold((data) {
await _getNextLevelUseCase(QuestionParams()).then(
(value) => value.fold(
(data) {
add(GetLevelEvent('${data.id}', context)); add(GetLevelEvent('${data.id}', context));
}, (error) {
},
(error) {
goToLevelPage(context: MyContext.get); goToLevelPage(context: MyContext.get);
}, },
), ),
); );
} }
void showingAnswerSequence({required bool show}){
void showingAnswerSequence({required bool show}) {
showAnswerSequence = show; showAnswerSequence = show;
} }
@ -246,7 +256,10 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
} }
/// ------------Event Calls------------ /// ------------Event Calls------------
FutureOr<void> _getLevelEvent(GetLevelEvent event, Emitter<QuestionState> emit) async {
FutureOr<void> _getLevelEvent(
GetLevelEvent event,
Emitter<QuestionState> emit,
) async {
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(
@ -257,16 +270,18 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
title: data.title, title: data.title,
questions: [ questions: [
...?data.questions, ...?data.questions,
QuestionEntity(order: (data.questions?.length ?? 0) + 1)
QuestionEntity(order: (data.questions?.length ?? 0) + 1),
], ],
); );
emit(state.copyWith(
emit(
state.copyWith(
getQuestionStatus: const BaseComplete(''), getQuestionStatus: const BaseComplete(''),
levelEntity: level, levelEntity: level,
currentQuestion: data.questions?.first, currentQuestion: data.questions?.first,
showAnswers: false, showAnswers: false,
correctAnswer: false, correctAnswer: false,
));
),
);
imageAnimationController?.forward(); imageAnimationController?.forward();
changeGlobeState(key: MyAnimations.globeStateSpeaking); changeGlobeState(key: MyAnimations.globeStateSpeaking);
await playQuestionAudio(); await playQuestionAudio();
@ -276,15 +291,19 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
}); });
}, },
(error) { (error) {
emit(state.copyWith(getQuestionStatus: BaseError(error.errorMessage)));
emit(
state.copyWith(getQuestionStatus: BaseError(error.errorMessage)),
);
}, },
); );
}, },
); );
} }
FutureOr<void> _chooseAnswerEvent(ChooseAnswerEvent event,
Emitter<QuestionState> emit,) async {
FutureOr<void> _chooseAnswerEvent(
ChooseAnswerEvent event,
Emitter<QuestionState> emit,
) async {
emit(state.copyWith(correctAnswer: event.chooseCorrectAnswer)); emit(state.copyWith(correctAnswer: event.chooseCorrectAnswer));
if (event.chooseCorrectAnswer) { if (event.chooseCorrectAnswer) {
@ -292,24 +311,29 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
await showAnswerDialog( await showAnswerDialog(
context: MyContext.get, context: MyContext.get,
correctAudio: state.currentQuestion?.correctAudio, correctAudio: state.currentQuestion?.correctAudio,
answerEntity: state.currentQuestion?.answers?.firstWhereOrNull((e) =>
e.order == event.correctAnswer) ?? AnswerEntity(),
answerEntity:
state.currentQuestion?.answers?.firstWhereOrNull(
(e) => e.order == event.correctAnswer,
) ??
AnswerEntity(),
showConfetti: true, showConfetti: true,
); );
answerAnimationController?.reverse(); answerAnimationController?.reverse();
await Future.delayed(const Duration(seconds: 1), () async { await Future.delayed(const Duration(seconds: 1), () async {
final QuestionEntity? findPreQuestion = state.currentQuestion; final QuestionEntity? findPreQuestion = state.currentQuestion;
final int findIndex = (findPreQuestion?.order ?? 1); final int findIndex = (findPreQuestion?.order ?? 1);
emit(
state.copyWith(
currentQuestion: state.levelEntity?.questions?[findIndex],
),
);
if (state.currentQuestion?.order ==
state.levelEntity?.questions?.length) {
final newCurrentQuestion = state.levelEntity?.questions?[findIndex];
final isLastQuestion =
newCurrentQuestion?.order == state.levelEntity?.questions?.length;
if (isLastQuestion) {
playDiamondAudio(); playDiamondAudio();
await Future.delayed(const Duration(milliseconds: 400));
}
emit(state.copyWith(currentQuestion: newCurrentQuestion));
if (isLastQuestion) {
int currentLevel = int.parse( int currentLevel = int.parse(
LocalStorage.readData(key: MyConstants.currentLevel) ?? '1');
LocalStorage.readData(key: MyConstants.currentLevel) ?? '1',
);
if (state.levelEntity?.order == currentLevel) { if (state.levelEntity?.order == currentLevel) {
++currentLevel; ++currentLevel;
await LocalStorage.saveData( await LocalStorage.saveData(
@ -334,7 +358,10 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
changeGlobeState(key: MyAnimations.globeStateWrong); changeGlobeState(key: MyAnimations.globeStateWrong);
playWrongAudio(); playWrongAudio();
await Future.delayed(const Duration(milliseconds: 1500), () { await Future.delayed(const Duration(milliseconds: 1500), () {
if (globeAnimationController?.value ==
globeStates[MyAnimations.globeStateWrong]) {
changeGlobeState(key: MyAnimations.globeStateAfterWrong); changeGlobeState(key: MyAnimations.globeStateAfterWrong);
}
}); });
} }
} }

26
lib/features/question/presentation/ui/question_page.dart

@ -14,9 +14,29 @@ import 'package:hadi_hoda_flutter/features/question/presentation/ui/screens/diam
import 'package:hadi_hoda_flutter/features/question/presentation/ui/screens/question_screen.dart'; import 'package:hadi_hoda_flutter/features/question/presentation/ui/screens/question_screen.dart';
import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/glassy_button.dart'; import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/glassy_button.dart';
import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/question_title.dart'; import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/question_title.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
class QuestionPage extends StatelessWidget {
class QuestionPage extends StatefulWidget {
const QuestionPage({super.key}); const QuestionPage({super.key});
static const double _muteThreshold = 0.001;
@override
State<QuestionPage> createState() => _QuestionPageState();
}
class _QuestionPageState extends State<QuestionPage> {
@override
void initState() {
WakelockPlus.enable();
super.initState();
}
@override
void dispose() {
WakelockPlus.disable();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -106,7 +126,9 @@ class QuestionPage extends StatelessWidget {
initialData: 1, initialData: 1,
stream: context.read<QuestionBloc>().volumeStream, stream: context.read<QuestionBloc>().volumeStream,
builder: (context, snapshot) => GlassyButton( builder: (context, snapshot) => GlassyButton(
image: snapshot.data == 0 ? MyAssets.unMusic : MyAssets.music,
image: (snapshot.data ?? 1) <= QuestionPage._muteThreshold
? MyAssets.unMusic
: MyAssets.music,
onTap: () => context.read<QuestionBloc>().changeMute(), onTap: () => context.read<QuestionBloc>().changeMute(),
), ),
), ),

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

@ -81,6 +81,7 @@ class _AnswerScreenState extends State<AnswerScreen> {
return Stack( return Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
Container(color: Colors.black12),
Center( Center(
child: Padding( child: Padding(
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(

14
lib/features/question/presentation/ui/screens/diamond_screen.dart

@ -160,17 +160,17 @@ class DiamondScreen extends StatelessWidget {
spacing: MySpaces.s12, spacing: MySpaces.s12,
children: [ children: [
Expanded( Expanded(
child: MyWhiteButton(
child: MyYellowButton(
onTap: () => context.read<QuestionBloc>().goToLevelPage(context: context), onTap: () => context.read<QuestionBloc>().goToLevelPage(context: context),
title: context.translate.map, title: context.translate.map,
), ),
), ),
Expanded(
child: MyYellowButton(
onTap: () => context.read<QuestionBloc>().getNextLevelEvent(context: context),
title: context.translate.next,
),
),
// Expanded(
// child: MyYellowButton(
// onTap: () => context.read<QuestionBloc>().getNextLevelEvent(context: context),
// title: context.translate.next,
// ),
// ),
], ],
); );
} }

56
lib/features/question/presentation/ui/screens/question_screen.dart

@ -7,8 +7,10 @@ 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/common_ui/resources/my_colors.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/constants/my_constants.dart';
import 'package:hadi_hoda_flutter/core/utils/app_life_cycle.dart';
import 'package:hadi_hoda_flutter/core/utils/gap.dart'; import 'package:hadi_hoda_flutter/core/utils/gap.dart';
import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; import 'package:hadi_hoda_flutter/core/utils/local_storage.dart';
import 'package:hadi_hoda_flutter/core/utils/my_device.dart';
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; import 'package:hadi_hoda_flutter/core/utils/my_localization.dart';
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; import 'package:hadi_hoda_flutter/core/utils/screen_size.dart';
import 'package:hadi_hoda_flutter/core/widgets/animations/fade_anim.dart'; import 'package:hadi_hoda_flutter/core/widgets/animations/fade_anim.dart';
@ -34,6 +36,7 @@ class QuestionScreen extends StatefulWidget {
class _QuestionScreenState extends State<QuestionScreen> class _QuestionScreenState extends State<QuestionScreen>
with TickerProviderStateMixin, WidgetsBindingObserver { with TickerProviderStateMixin, WidgetsBindingObserver {
late final isTablet = MyDevice.isTablet(context);
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -42,22 +45,22 @@ class _QuestionScreenState extends State<QuestionScreen>
.read<QuestionBloc>() .read<QuestionBloc>()
.answerAnimationController = AnimationController( .answerAnimationController = AnimationController(
vsync: this, vsync: this,
duration: const Duration(milliseconds: 500),
reverseDuration: const Duration(milliseconds: 500),
duration: const Duration(milliseconds: 350),
reverseDuration: const Duration(milliseconds: 350),
); );
context.read<QuestionBloc>().imageAnimationController = AnimationController( context.read<QuestionBloc>().imageAnimationController = AnimationController(
vsync: this, vsync: this,
duration: const Duration(milliseconds: 500),
reverseDuration: const Duration(milliseconds: 500),
duration: const Duration(milliseconds: 200),
reverseDuration: const Duration(milliseconds: 200),
); );
if (LocalStorage.readData(key: MyConstants.firstShowcase) == 'true') { if (LocalStorage.readData(key: MyConstants.firstShowcase) == 'true') {
context.read<QuestionBloc>().imageAnimationController?.forward(); context.read<QuestionBloc>().imageAnimationController?.forward();
} }
context.read<QuestionBloc>().globeAnimationController = AnimationController( context.read<QuestionBloc>().globeAnimationController = AnimationController(
vsync: this, vsync: this,
duration: const Duration(milliseconds: 500),
reverseDuration: const Duration(milliseconds: 500),
duration: const Duration(milliseconds: 350),
reverseDuration: const Duration(milliseconds: 350),
); );
} }
@ -68,6 +71,7 @@ class _QuestionScreenState extends State<QuestionScreen>
state == AppLifecycleState.inactive) { state == AppLifecycleState.inactive) {
context.read<QuestionBloc>().pauseBackgroundPlaying(); context.read<QuestionBloc>().pauseBackgroundPlaying();
} else if (state == AppLifecycleState.resumed) { } else if (state == AppLifecycleState.resumed) {
if (AppLifeCycleController.suppressAutoResumeAudio) return;
context.read<QuestionBloc>().playBackgroundPlaying(); context.read<QuestionBloc>().playBackgroundPlaying();
} }
} }
@ -92,9 +96,7 @@ class _QuestionScreenState extends State<QuestionScreen>
} }
Widget _questionImage(BuildContext context) { Widget _questionImage(BuildContext context) {
return FadeAnimDelayed(
duration: const Duration(seconds: 1),
child: FadeAnimController(
return FadeAnimController(
controller: context.read<QuestionBloc>().imageAnimationController!, controller: context.read<QuestionBloc>().imageAnimationController!,
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
@ -110,7 +112,6 @@ class _QuestionScreenState extends State<QuestionScreen>
], ],
), ),
), ),
),
); );
} }
@ -189,9 +190,10 @@ class _QuestionScreenState extends State<QuestionScreen>
null) { null) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} else { } else {
return SizedBox(
width: 180,
height: 250,
return Container(
alignment: isTablet ? Alignment.center : null,
width: isTablet ? 320 : 180,
height: isTablet ? 300 :250,
child: SlideAnim( child: SlideAnim(
controller: context controller: context
.read<QuestionBloc>() .read<QuestionBloc>()
@ -240,9 +242,10 @@ class _QuestionScreenState extends State<QuestionScreen>
null) { null) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} else { } else {
return SizedBox(
width: 180,
height: 250,
return Container(
alignment: isTablet ? Alignment.center : null,
width: isTablet ? 320 : 180,
height: isTablet ? 300 :250,
child: SlideAnim( child: SlideAnim(
controller: context controller: context
.read<QuestionBloc>() .read<QuestionBloc>()
@ -297,9 +300,10 @@ class _QuestionScreenState extends State<QuestionScreen>
null) { null) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} else { } else {
return SizedBox(
width: 180,
height: 250,
return Container(
alignment: isTablet ? Alignment.center : null,
width: isTablet ? 320 : 180,
height: isTablet ? 300 :250,
child: SlideAnim( child: SlideAnim(
controller: context controller: context
.read<QuestionBloc>() .read<QuestionBloc>()
@ -354,9 +358,10 @@ class _QuestionScreenState extends State<QuestionScreen>
null) { null) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} else { } else {
return SizedBox(
width: 180,
height: 250,
return Container(
alignment: isTablet ? Alignment.center : null,
width: isTablet ? 320 : 180,
height: isTablet ? 300 :250,
child: SlideAnim( child: SlideAnim(
controller: context controller: context
.read<QuestionBloc>() .read<QuestionBloc>()
@ -406,9 +411,10 @@ class _QuestionScreenState extends State<QuestionScreen>
null) { null) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} else { } else {
return SizedBox(
width: 180,
height: 250,
return Container(
alignment: isTablet ? Alignment.center : null,
width: isTablet ? 320 : 180,
height: isTablet ? 300 :250,
child: SlideAnim( child: SlideAnim(
controller: context controller: context
.read<QuestionBloc>() .read<QuestionBloc>()

2
lib/features/splash/presentation/bloc/splash_bloc.dart

@ -24,7 +24,7 @@ class SplashBloc extends Bloc<SplashEvent, SplashState> {
const Duration(seconds: 2), const Duration(seconds: 2),
() { () {
if (context.mounted) { if (context.mounted) {
context.goNamed(Routes.homePage);
context.goNamed(Routes.introPage);
} }
}, },
); );

23
lib/features/splash/presentation/ui/splash_page.dart

@ -45,7 +45,7 @@ class _SplashPageState extends State<SplashPage> {
scale: 3, scale: 3,
repeat: ImageRepeat.repeat, repeat: ImageRepeat.repeat,
colorFilter: ColorFilter.mode( colorFilter: ColorFilter.mode(
Colors.white.withValues(alpha: 0.2),
Colors.white.withValues(alpha: 0.22),
BlendMode.srcIn, BlendMode.srcIn,
), ),
), ),
@ -53,7 +53,7 @@ class _SplashPageState extends State<SplashPage> {
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
// _image(),
Positioned.fill(child: _image()),
_loading(context), _loading(context),
], ],
), ),
@ -63,17 +63,28 @@ class _SplashPageState extends State<SplashPage> {
Widget _image() { Widget _image() {
return const Stack( return const Stack(
alignment: Alignment.center,
children: [ children: [
MyImage( MyImage(
image: MyAssets.hadiHoda, image: MyAssets.hadiHoda,
), ),
PositionedDirectional(
start: MySpaces.s10,
top: MySpaces.s40,
Positioned(
right: 0,
left: 0,
top: 0,
bottom: 0,
child: Center(
child: Padding(
padding: EdgeInsets.only(bottom: 140, right: 235),
child: Opacity(
opacity: .94,
child: MyImage( child: MyImage(
image: MyAssets.globe, image: MyAssets.globe,
), ),
), ),
),
),
),
], ],
); );
} }
@ -82,6 +93,8 @@ class _SplashPageState extends State<SplashPage> {
Positioned _loading(BuildContext context) { Positioned _loading(BuildContext context) {
return Positioned( return Positioned(
bottom: MySpaces.s40, bottom: MySpaces.s40,
right: 0,
left: 0,
child: RotationAnim( child: RotationAnim(
child: MyImage( child: MyImage(
image: MyAssets.loading, image: MyAssets.loading,

107
lib/init_bindings.dart

@ -68,46 +68,99 @@ void initBindings() {
locator.registerSingleton<AppBloc>(AppBloc()); locator.registerSingleton<AppBloc>(AppBloc());
/// Blocs /// Blocs
locator.registerFactory<LanguageBloc>(() => LanguageBloc(locator(), locator(), locator()));
locator.registerFactory<LanguageBloc>(
() => LanguageBloc(locator(), locator(), locator()),
);
locator.registerFactory<DownloadBloc>( locator.registerFactory<DownloadBloc>(
() => DownloadBloc(locator(), locator(), locator(), locator(), locator()));
() => DownloadBloc(locator(), locator(), locator(), locator(), locator()),
);
/// Sample Feature /// Sample Feature
locator.registerLazySingleton<ISampleDatasource>(() => SampleDatasourceImpl(locator()));
locator.registerLazySingleton<ISampleRepository>(() => SampleRepositoryImpl(locator()));
locator.registerLazySingleton<GetSampleUseCase>(() => GetSampleUseCase(locator()));
locator.registerLazySingleton<ISampleDatasource>(
() => SampleDatasourceImpl(locator()),
);
locator.registerLazySingleton<ISampleRepository>(
() => SampleRepositoryImpl(locator()),
);
locator.registerLazySingleton<GetSampleUseCase>(
() => GetSampleUseCase(locator()),
);
/// Download Feature /// Download Feature
locator.registerLazySingleton<IDownloadDatasource>(() => DownloadDatasourceImpl(locator()));
locator.registerLazySingleton<IDownloadRepository>(() => DownloadRepositoryImpl(locator()));
locator.registerLazySingleton<SaveLevelsUseCase>(() => SaveLevelsUseCase(locator()));
locator.registerLazySingleton<BatchDownloadUseCase>(() => BatchDownloadUseCase(locator()));
locator.registerLazySingleton<LoadingStreamUseCase>(() => LoadingStreamUseCase(locator()));
locator.registerLazySingleton<CancelDownloadUseCase>(() => CancelDownloadUseCase(locator()));
locator.registerLazySingleton<GetLastDownloadedLevel>(() => GetLastDownloadedLevel(locator()));
locator.registerLazySingleton<IDownloadDatasource>(
() => DownloadDatasourceImpl(locator()),
);
locator.registerLazySingleton<IDownloadRepository>(
() => DownloadRepositoryImpl(locator()),
);
locator.registerLazySingleton<SaveLevelsUseCase>(
() => SaveLevelsUseCase(locator()),
);
locator.registerLazySingleton<BatchDownloadUseCase>(
() => BatchDownloadUseCase(locator()),
);
locator.registerLazySingleton<LoadingStreamUseCase>(
() => LoadingStreamUseCase(locator()),
);
locator.registerLazySingleton<CancelDownloadUseCase>(
() => CancelDownloadUseCase(locator()),
);
locator.registerLazySingleton<GetLastDownloadedLevel>(
() => GetLastDownloadedLevel(locator()),
);
/// Guider Feature /// Guider Feature
locator.registerLazySingleton<IGuiderDatasource>(() => const GuiderDatasourceImpl());
locator.registerLazySingleton<IGuiderRepository>(() => GuiderRepositoryImpl(locator()));
locator.registerLazySingleton<GetFirstLevelUseCase>(() => GetFirstLevelUseCase(locator()));
locator.registerLazySingleton<IGuiderDatasource>(
() => const GuiderDatasourceImpl(),
);
locator.registerLazySingleton<IGuiderRepository>(
() => GuiderRepositoryImpl(locator()),
);
locator.registerLazySingleton<GetFirstLevelUseCase>(
() => GetFirstLevelUseCase(locator()),
);
/// Question Feature /// Question Feature
locator.registerLazySingleton<IQuestionDatasource>(() => const QuestionDatasourceImpl());
locator.registerLazySingleton<IQuestionRepository>(() => QuestionRepositoryImpl(locator()));
locator.registerLazySingleton<GetLevelUseCase>(() => GetLevelUseCase(locator()));
locator.registerLazySingleton<GetNextLevelUseCase>(() => GetNextLevelUseCase(locator()));
locator.registerLazySingleton<IQuestionDatasource>(
() => const QuestionDatasourceImpl(),
);
locator.registerLazySingleton<IQuestionRepository>(
() => QuestionRepositoryImpl(locator()),
);
locator.registerLazySingleton<GetLevelUseCase>(
() => GetLevelUseCase(locator()),
);
locator.registerLazySingleton<GetNextLevelUseCase>(
() => GetNextLevelUseCase(locator()),
);
/// Level Feature /// Level Feature
locator.registerLazySingleton<ILevelDatasource>(() => const LocalLevelDatasourceImpl());
locator.registerLazySingleton<ILevelRepository>(() => LevelRepositoryImpl(locator()));
locator.registerLazySingleton<GetLevelsUseCase>(() => GetLevelsUseCase(locator()));
locator.registerLazySingleton<ILevelDatasource>(
() => LocalLevelDatasourceImpl(locator()),
);
locator.registerLazySingleton<ILevelRepository>(
() => LevelRepositoryImpl(locator()),
);
locator.registerLazySingleton<GetLevelsUseCase>(
() => GetLevelsUseCase(locator()),
);
/// Language Feature /// Language Feature
locator.registerLazySingleton<ILanguageDatasource>(() => LanguageDatasourceImpl(locator()));
locator.registerLazySingleton<ILanguageRepository>(() => LanguageRepositoryImpl(locator()));
locator.registerLazySingleton<GetLanguagesUseCase>(() => GetLanguagesUseCase(locator()));
locator.registerLazySingleton<GetSelectedLanguageUseCase>(() => GetSelectedLanguageUseCase(locator()));
locator.registerLazySingleton<SaveSelectedLanguageUseCase>(() => SaveSelectedLanguageUseCase(locator()));
locator.registerLazySingleton<ILanguageDatasource>(
() => LanguageDatasourceImpl(locator()),
);
locator.registerLazySingleton<ILanguageRepository>(
() => LanguageRepositoryImpl(locator()),
);
locator.registerLazySingleton<GetLanguagesUseCase>(
() => GetLanguagesUseCase(locator()),
);
locator.registerLazySingleton<GetSelectedLanguageUseCase>(
() => GetSelectedLanguageUseCase(locator()),
);
locator.registerLazySingleton<SaveSelectedLanguageUseCase>(
() => SaveSelectedLanguageUseCase(locator()),
);
} }
Future<void> initDataBase() async { Future<void> initDataBase() async {

3
lib/l10n/app_ar.arb

@ -36,5 +36,6 @@
"showcase_stepper": "هنا سترى الأسئلة الخاصة\nبهذه المرحلة للوصول\nإلى الماسة.", "showcase_stepper": "هنا سترى الأسئلة الخاصة\nبهذه المرحلة للوصول\nإلى الماسة.",
"showcase_hadith": "اطّلع على المصادر والأحاديث\nلهذا السؤال", "showcase_hadith": "اطّلع على المصادر والأحاديث\nلهذا السؤال",
"showcase_guide": "هذا دليل سيساعدك\nفي رحلتك.", "showcase_guide": "هذا دليل سيساعدك\nفي رحلتك.",
"reward": "جائزة"
"reward": "جائزة",
"downloading": "جارٍ التنزيل"
} }

3
lib/l10n/app_de.arb

@ -36,5 +36,6 @@
"showcase_stepper": "Hier siehst du die\nFragen für diese\nStufe, um den\nDiamanten zu erreichen.", "showcase_stepper": "Hier siehst du die\nFragen für diese\nStufe, um den\nDiamanten zu erreichen.",
"showcase_hadith": "Quellen und\nHadithe zu dieser\nFrage ansehen.", "showcase_hadith": "Quellen und\nHadithe zu dieser\nFrage ansehen.",
"showcase_guide": "Dies ist eine Anleitung,\ndie dir hilft.", "showcase_guide": "Dies ist eine Anleitung,\ndie dir hilft.",
"reward": "belohnen"
"reward": "belohnen",
"downloading": "Wird heruntergeladen"
} }

4
lib/l10n/app_en.arb

@ -36,5 +36,7 @@
"showcase_stepper": "Here you will see the\nquestions for this\nstage to reach the\ndiamond.", "showcase_stepper": "Here you will see the\nquestions for this\nstage to reach the\ndiamond.",
"showcase_hadith": "View sources and\nhadiths for this\nquestion", "showcase_hadith": "View sources and\nhadiths for this\nquestion",
"showcase_guide": "This is a guide that will\nhelp you.", "showcase_guide": "This is a guide that will\nhelp you.",
"reward": "Reward"
"reward": "Reward",
"coming_soon": "Next stages will open soon",
"downloading": "Downloading"
} }

3
lib/l10n/app_fr.arb

@ -36,5 +36,6 @@
"showcase_stepper": "Ici, tu verras les\nquestions de cette\nétape pour atteindre\nle diamant.", "showcase_stepper": "Ici, tu verras les\nquestions de cette\nétape pour atteindre\nle diamant.",
"showcase_hadith": "Consulte les sources et\nles hadiths pour cette\nquestion", "showcase_hadith": "Consulte les sources et\nles hadiths pour cette\nquestion",
"showcase_guide": "Ceci est un guide\nqui t’aidera.", "showcase_guide": "Ceci est un guide\nqui t’aidera.",
"reward": "récompense"
"reward": "récompense",
"downloading": "Téléchargement"
} }

12
lib/l10n/app_localizations.dart

@ -327,6 +327,18 @@ abstract class AppLocalizations {
/// In en, this message translates to: /// In en, this message translates to:
/// **'Reward'** /// **'Reward'**
String get reward; String get reward;
/// No description provided for @coming_soon.
///
/// In en, this message translates to:
/// **'Next stages will open soon'**
String get coming_soon;
/// No description provided for @downloading.
///
/// In en, this message translates to:
/// **'Downloading'**
String get downloading;
} }
class _AppLocalizationsDelegate class _AppLocalizationsDelegate

6
lib/l10n/app_localizations_ar.dart

@ -122,4 +122,10 @@ class AppLocalizationsAr extends AppLocalizations {
@override @override
String get reward => 'جائزة'; String get reward => 'جائزة';
@override
String get coming_soon => 'Next stages will open soon';
@override
String get downloading => 'جارٍ التنزيل';
} }

6
lib/l10n/app_localizations_de.dart

@ -129,4 +129,10 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get reward => 'belohnen'; String get reward => 'belohnen';
@override
String get coming_soon => 'Next stages will open soon';
@override
String get downloading => 'Wird heruntergeladen';
} }

6
lib/l10n/app_localizations_en.dart

@ -126,4 +126,10 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get reward => 'Reward'; String get reward => 'Reward';
@override
String get coming_soon => 'Next stages will open soon';
@override
String get downloading => 'Downloading';
} }

6
lib/l10n/app_localizations_fr.dart

@ -127,4 +127,10 @@ class AppLocalizationsFr extends AppLocalizations {
@override @override
String get reward => 'récompense'; String get reward => 'récompense';
@override
String get coming_soon => 'Next stages will open soon';
@override
String get downloading => 'Téléchargement';
} }

6
lib/l10n/app_localizations_ru.dart

@ -127,4 +127,10 @@ class AppLocalizationsRu extends AppLocalizations {
@override @override
String get reward => 'награда'; String get reward => 'награда';
@override
String get coming_soon => 'Next stages will open soon';
@override
String get downloading => 'Загрузка';
} }

6
lib/l10n/app_localizations_tr.dart

@ -126,4 +126,10 @@ class AppLocalizationsTr extends AppLocalizations {
@override @override
String get reward => 'ödül'; String get reward => 'ödül';
@override
String get coming_soon => 'Next stages will open soon';
@override
String get downloading => 'İndiriliyor';
} }

3
lib/l10n/app_ru.arb

@ -36,5 +36,6 @@
"showcase_stepper": "Здесь ты увидишь\nвопросы этого этапа,\nчтобы получить\nалмаз.", "showcase_stepper": "Здесь ты увидишь\nвопросы этого этапа,\nчтобы получить\nалмаз.",
"showcase_hadith": "Просмотри источники и\nхадисы по этому вопросу", "showcase_hadith": "Просмотри источники и\nхадисы по этому вопросу",
"showcase_guide": "Это руководство,\nкоторое поможет тебе.", "showcase_guide": "Это руководство,\nкоторое поможет тебе.",
"reward": "награда"
"reward": "награда",
"downloading": "Загрузка"
} }

3
lib/l10n/app_tr.arb

@ -36,5 +36,6 @@
"showcase_stepper": "Burada, elmasa ulaşmak\niçin bu aşamadaki\nsoruları göreceksin.", "showcase_stepper": "Burada, elmasa ulaşmak\niçin bu aşamadaki\nsoruları göreceksin.",
"showcase_hadith": "Bu soruya ait\nkaynakları ve hadisleri\nincele", "showcase_hadith": "Bu soruya ait\nkaynakları ve hadisleri\nincele",
"showcase_guide": "Bu, sana yardımcı olacak\nbir rehberdir.", "showcase_guide": "Bu, sana yardımcı olacak\nbir rehberdir.",
"reward": "ödül"
"reward": "ödül",
"downloading": "İndiriliyor"
} }

7
pubspec.yaml

@ -10,6 +10,7 @@ dependencies:
auto_size_text: ^3.0.0 auto_size_text: ^3.0.0
bloc: ^9.0.0 bloc: ^9.0.0
collection: ^1.19.1 collection: ^1.19.1
connectivity_plus: ^7.0.0
dio: ^5.9.0 dio: ^5.9.0
easy_stepper: ^0.8.5+1 easy_stepper: ^0.8.5+1
equatable: ^2.0.7 equatable: ^2.0.7
@ -32,7 +33,9 @@ dependencies:
pretty_dio_logger: ^1.4.0 pretty_dio_logger: ^1.4.0
shared_preferences: ^2.5.3 shared_preferences: ^2.5.3
showcaseview: ^5.0.1 showcaseview: ^5.0.1
url_launcher: ^6.3.2
vector_graphics: ^1.1.19 vector_graphics: ^1.1.19
wakelock_plus: ^1.4.0
wheel_chooser: ^1.0.1 wheel_chooser: ^1.0.1
dev_dependencies: dev_dependencies:
@ -52,6 +55,7 @@ flutter:
- assets/images/ - assets/images/
- assets/animations/ - assets/animations/
- assets/audios/ - assets/audios/
- assets/audios/incorrect_answers/
- assets/videos/ - assets/videos/
- assets/svg/ - assets/svg/
@ -77,3 +81,6 @@ flutter:
weight: 700 weight: 700
- asset: assets/fonts/Baloo2-ExtraBold.ttf - asset: assets/fonts/Baloo2-ExtraBold.ttf
weight: 800 weight: 800
- family: NotoSansArabic
fonts:
- asset: assets/fonts/NotoSansArabic.ttf
Loading…
Cancel
Save