Browse Source

Merge pull request 'feature/language' (#12) from feature/language into develop

Reviewed-on: https://git.nwhco.ir/amirreza.chegini/hade_hoda_flutter/pulls/12
pull/13/head
amirreza.chegini 4 days ago
parent
commit
7cf29bc945
  1. 44
      assets/images/button.svg
  2. 51
      assets/images/button_2.svg
  3. 3
      assets/images/lang.svg
  4. 77
      assets/images/start.svg
  5. 4
      lib/common_ui/resources/my_assets.dart
  6. 5
      lib/common_ui/resources/my_text_style.dart
  7. 1
      lib/core/constants/my_constants.dart
  8. 20
      lib/core/middleware/auth_middleware.dart
  9. 21
      lib/core/middleware/language_middleware.dart
  10. 17
      lib/core/params/language_params.dart
  11. 13
      lib/core/routers/my_routes.dart
  12. 18
      lib/core/widgets/button/enum/button_type.dart
  13. 50
      lib/core/widgets/button/my_button.dart
  14. 2
      lib/features/home/presentation/ui/home_page.dart
  15. 2
      lib/features/intro/presentation/bloc/intro_bloc.dart
  16. 46
      lib/features/language/data/datasource/language_datasource.dart
  17. 29
      lib/features/language/data/repository_impl/language_repository_impl.dart
  18. 17
      lib/features/language/domain/entity/language_entity.dart
  19. 8
      lib/features/language/domain/repository/language_repository.dart
  20. 17
      lib/features/language/domain/usecases/save_levels_usecase.dart
  21. 60
      lib/features/language/presentation/bloc/language_bloc.dart
  22. 16
      lib/features/language/presentation/bloc/language_event.dart
  23. 22
      lib/features/language/presentation/bloc/language_state.dart
  24. 157
      lib/features/language/presentation/ui/language_page.dart
  25. 24
      lib/features/level/data/datasource/level_datasource.dart
  26. 2
      lib/features/level/domain/entity/level_entity.dart
  27. 2
      lib/features/level/domain/entity/level_entity.g.dart
  28. 17
      lib/features/level/domain/entity/total_data_entity.dart
  29. 44
      lib/features/level/domain/entity/total_data_entity.g.dart
  30. 2
      lib/features/question/domain/entity/answer_entity.dart
  31. 2
      lib/features/question/domain/entity/answer_entity.g.dart
  32. 2
      lib/features/question/domain/entity/file_entity.dart
  33. 2
      lib/features/question/domain/entity/file_entity.g.dart
  34. 2
      lib/features/question/domain/entity/question_entity.dart
  35. 2
      lib/features/question/domain/entity/question_entity.g.dart
  36. 15
      lib/init_bindings.dart
  37. 4
      lib/l10n/app_en.arb
  38. 12
      lib/l10n/app_localizations.dart
  39. 6
      lib/l10n/app_localizations_en.dart

44
assets/images/button.svg

@ -0,0 +1,44 @@
<svg width="198" height="93" viewBox="0 0 198 93" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_66_104)">
<path d="M188.461 75.3351C188.278 75.5102 188.081 75.673 187.872 75.8222C171.323 88.1843 27.7109 87.7476 7.66766 76.6452C6.79485 76.2265 6.06847 75.5921 5.5714 74.8144C-1.50808 61.3774 2.33508 20.7639 13.5887 11.9795C25.6882 2.57354 170.109 -1.69272 185.041 10.2998C196.055 19.0507 201.278 63.0402 188.461 75.3351Z" fill="url(#paint0_linear_66_104)"/>
</g>
<path d="M5.62633 72.1439C12.4668 85.161 174.357 86.2024 188.461 72.6813C201.332 60.3865 196.055 16.3801 185.096 7.61248C170.164 -4.34649 25.7431 -0.0970216 13.662 9.29211C2.38999 18.0598 -1.45315 58.6732 5.62633 72.1439Z" fill="url(#paint1_linear_66_104)"/>
<path d="M183.044 69.9851C182.872 70.1391 182.688 70.2824 182.492 70.4137C166.952 81.2903 32.1063 80.906 13.2866 71.1378C12.4671 70.7694 11.785 70.2112 11.3183 69.527C4.67096 57.7046 8.27953 21.9714 18.8462 14.2425C30.2071 5.96688 165.813 2.21327 179.833 12.7647C190.175 20.4641 195.078 59.1676 183.044 69.9851Z" fill="url(#paint2_linear_66_104)"/>
<path d="M11.3698 67.1773C17.7927 78.6302 169.801 79.5465 183.044 67.6502C195.13 56.8327 190.175 18.1144 179.884 10.4003C165.864 -0.121644 30.2586 3.61719 18.915 11.8781C8.33101 19.5922 4.72246 55.3254 11.3698 67.1773Z" fill="url(#paint3_linear_66_104)"/>
<path style="mix-blend-mode:screen" opacity="0.5" d="M35.3606 9.55896C35.3606 9.55896 129.168 -2.28615 178.184 10.2901C181.245 13.8661 183.203 17.563 184.011 21.2907C171.774 25.9233 155.491 28.399 138.38 28.2285C112.852 28.1796 88.6321 14.0536 67.1617 11.752C56.7526 10.4783 46.0686 9.74151 35.3606 9.55896Z" fill="url(#paint4_linear_66_104)"/>
<defs>
<filter id="filter0_d_66_104" x="0.37037" y="2.96387" width="197.232" height="89.8103" 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.11111"/>
<feGaussianBlur stdDeviation="0.814815"/>
<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_66_104"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_66_104" result="shape"/>
</filter>
<linearGradient id="paint0_linear_66_104" x1="99.0205" y1="85.0434" x2="99.0205" y2="2.95985" gradientUnits="userSpaceOnUse">
<stop stop-color="#8B9AD1"/>
<stop offset="1" stop-color="#CEE0FF"/>
</linearGradient>
<linearGradient id="paint1_linear_66_104" x1="99.0202" y1="82.3728" x2="99.0202" y2="0.289293" gradientUnits="userSpaceOnUse">
<stop stop-color="#CADCFF"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<linearGradient id="paint2_linear_66_104" x1="99.0632" y1="78.5268" x2="99.0632" y2="6.30677" gradientUnits="userSpaceOnUse">
<stop stop-color="#07A7FD"/>
<stop offset="1" stop-color="#00DDF4"/>
</linearGradient>
<linearGradient id="paint3_linear_66_104" x1="99.0629" y1="76.1771" x2="99.0629" y2="3.95708" gradientUnits="userSpaceOnUse">
<stop stop-color="#01EDF2"/>
<stop offset="1" stop-color="#08A3FF"/>
</linearGradient>
<linearGradient id="paint4_linear_66_104" x1="35.1888" y1="13.4594" x2="184.067" y2="20.0165" gradientUnits="userSpaceOnUse">
<stop stop-color="#7ED140"/>
<stop offset="0.11" stop-color="#74C13B"/>
<stop offset="0.34" stop-color="#5B972E"/>
<stop offset="0.65" stop-color="#325319"/>
<stop offset="1"/>
</linearGradient>
</defs>
</svg>

51
assets/images/button_2.svg

@ -0,0 +1,51 @@
<svg width="198" height="94" viewBox="0 0 198 94" fill="none" xmlns="http://www.w3.org/2000/svg">
<g filter="url(#filter0_d_62_3)">
<path d="M188.649 75.7216C188.465 75.8967 188.269 76.0595 188.06 76.2088C171.511 88.5762 27.8983 88.1393 7.85512 77.0322C6.98232 76.6133 6.25594 75.9787 5.75886 75.2006C-1.32062 61.7579 2.52254 21.1271 13.7762 12.3389C25.8756 2.92899 170.297 -1.33908 185.228 10.6586C196.243 19.4132 201.465 63.4214 188.649 75.7216Z" fill="url(#paint0_linear_62_3)"/>
</g>
<path d="M5.81368 72.529C12.6541 85.5516 174.544 86.5934 188.648 73.0667C201.52 60.7665 196.243 16.7415 185.283 7.97009C170.352 -3.99397 25.9304 0.257303 13.8493 9.65043C2.57734 18.4218 -1.2658 59.0526 5.81368 72.529Z" fill="url(#paint1_linear_62_3)"/>
<path d="M10.8713 66.6295C17.0865 78.3919 167.447 81.7526 180.246 69.4861C191.885 58.3118 187.16 18.3364 177.211 10.3716C163.641 -0.483467 30.1054 7.1453 19.0724 15.6815C8.83017 23.6631 4.43537 54.3798 10.8713 66.6295Z" fill="url(#paint2_radial_62_3)"/>
<path d="M11.3678 64.5472C17.5646 75.9399 167.043 76.7465 179.786 64.9001C191.37 54.1459 186.829 18.5393 176.917 10.8602C163.402 0.374815 30.2893 7.73473 19.3483 15.9012C9.21636 23.6644 4.96866 52.7344 11.3678 64.5472Z" fill="url(#paint3_linear_62_3)"/>
<path d="M13.2427 63.5391C19.2924 74.5957 165.534 75.3855 178.001 63.8919C189.347 53.4402 184.879 18.8923 175.188 11.4316C161.985 1.26547 31.7596 8.40694 21.0393 16.4726C11.0545 23.8661 6.95387 52.0287 13.2427 63.5391Z" fill="url(#paint4_linear_62_3)"/>
<path style="mix-blend-mode:screen" d="M20.6719 16.4554C20.6719 16.4554 122.653 -2.4485 175.556 11.7C178.81 15.8078 181.113 20.4792 182.323 25.4284C167.263 29.7983 151.627 32.2964 135.838 32.8555C108.623 33.9814 80.1763 19.9337 57.1909 17.8333C45.0721 16.5474 32.8654 16.0869 20.6719 16.4554Z" fill="url(#paint5_linear_62_3)"/>
<path d="M16.847 21.2277C18.4504 18.5512 20.9243 16.3963 23.9265 15.0608C28.8913 12.91 43.0135 10.7423 43.8226 11.2968C44.6317 11.8514 36.9454 14.3719 32.9551 15.7162C28.9649 17.0604 18.8697 22.2527 16.847 21.2277Z" fill="white"/>
<defs>
<filter id="filter0_d_62_3" x="0.557136" y="3.31949" width="197.234" height="89.8486" 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_62_3"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_62_3" result="shape"/>
</filter>
<linearGradient id="paint0_linear_62_3" x1="99.208" y1="85.4339" x2="99.208" y2="3.31547" gradientUnits="userSpaceOnUse">
<stop stop-color="#8B9AD1"/>
<stop offset="1" stop-color="#CEE0FF"/>
</linearGradient>
<linearGradient id="paint1_linear_62_3" x1="99.2077" y1="82.7623" x2="99.2077" y2="0.643783" gradientUnits="userSpaceOnUse">
<stop stop-color="#CADCFF"/>
<stop offset="1" stop-color="white"/>
</linearGradient>
<radialGradient id="paint2_radial_62_3" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(100.385 72.1411) scale(69.2502 63.2818)">
<stop stop-color="#FFBD00"/>
<stop offset="1" stop-color="#FF772C"/>
</radialGradient>
<linearGradient id="paint3_linear_62_3" x1="97.48" y1="73.453" x2="97.48" y2="5.68471" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFF300"/>
<stop offset="1" stop-color="#FF9700"/>
</linearGradient>
<linearGradient id="paint4_linear_62_3" x1="97.4793" y1="72.176" x2="97.4793" y2="6.40733" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFCC00"/>
<stop offset="1" stop-color="#FF9700"/>
</linearGradient>
<linearGradient id="paint5_linear_62_3" x1="20.6719" y1="19.6312" x2="182.323" y2="19.6312" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF9840"/>
<stop offset="0.1" stop-color="#EF8E3C"/>
<stop offset="0.29" stop-color="#C57531"/>
<stop offset="0.55" stop-color="#814D20"/>
<stop offset="0.88" stop-color="#241609"/>
<stop offset="1"/>
</linearGradient>
</defs>
</svg>

3
assets/images/lang.svg

@ -0,0 +1,3 @@
<svg width="26" height="25" viewBox="0 0 26 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.2676 0.871063H10.5279V4.85222H13.7129V6.44469H3.36184C2.00825 6.44469 0.973145 7.47979 0.973145 8.83339V17.5919C0.973145 18.9455 2.00825 19.9806 3.36184 19.9806H4.15807V24.0414L9.17434 19.9806H14.5091V14.407H23.2676C24.6212 14.407 25.6563 13.3719 25.6563 12.0183V3.25976C25.6563 1.90617 24.6212 0.871063 23.2676 0.871063ZM9.57245 16.7161L9.17434 15.4421H6.70602L6.22828 16.7161H4.31732L6.94489 9.62962H8.85584L11.4834 16.7161H9.57245ZM21.6752 10.4259V12.0183C20.6401 12.0183 19.5254 11.6998 18.5699 11.2221C17.6144 11.6998 16.4997 11.9387 15.3849 12.0183L15.3053 10.4259C15.8627 10.4259 16.4201 10.3462 16.9774 10.187C16.2608 9.47037 15.7831 8.59452 15.5442 7.63904H17.2163C17.4552 8.35565 17.9329 8.91301 18.4903 9.39075C19.3661 8.67414 19.9235 7.63904 20.0031 6.44469H15.2257V4.85222H17.6144V3.25976H19.2069V4.85222H21.8344L21.9141 5.64846C21.9937 7.32054 21.3567 8.99263 20.1623 10.187C20.7197 10.3462 21.1974 10.4259 21.6752 10.4259Z" fill="#847AC4"/>
</svg>

77
assets/images/start.svg
File diff suppressed because it is too large
View File

4
lib/common_ui/resources/my_assets.dart

@ -8,7 +8,8 @@ class MyAssets {
static const String hadiHoda = 'assets/images/hadi_hoda.png';
static const String musicOff = 'assets/images/music_off.svg';
static const String musicOn = 'assets/images/music_on.svg';
static const String start = 'assets/images/start.svg';
static const String button = 'assets/images/button.svg';
static const String button2 = 'assets/images/button_2.svg';
static const String theme = 'assets/images/theme.svg';
static const String facebook = 'assets/images/facebook.svg';
static const String whatsapp = 'assets/images/whatsapp.svg';
@ -36,4 +37,5 @@ class MyAssets {
static const String play = 'assets/images/play.svg';
static const String homeButton = 'assets/images/home_button.png';
static const String doneRounded = 'assets/images/done_rounded.svg';
static const String lang = 'assets/images/lang.svg';
}

5
lib/common_ui/resources/my_text_style.dart

@ -18,6 +18,11 @@ class DinoKids {
fontSize: 26,
fontWeight: FontWeight.w400,
);
static const TextStyle regular45 = TextStyle(
fontFamily: fontFamily,
fontSize: 45,
fontWeight: FontWeight.w400,
);
}
class Marhey {

1
lib/core/constants/my_constants.dart

@ -8,4 +8,5 @@ class MyConstants {
static const String levelBox = 'LEVEL_BOX';
static const String downloadCompleted = 'DOWNLOAD_COMPLETED';
static const String extractCompleted = 'EXTRACT_COMPLETED';
static const String selectLanguage = 'SELECT_LANGUAGE';
}

20
lib/core/middleware/auth_middleware.dart

@ -1,20 +0,0 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:hadi_hoda_flutter/core/auth_storage/auth_storage.dart';
import 'package:hadi_hoda_flutter/core/routers/my_routes.dart';
import 'package:go_router/go_router.dart';
class AuthMiddleware {
static const AuthMiddleware _i = AuthMiddleware._internal();
const AuthMiddleware._internal();
factory AuthMiddleware() => _i;
static FutureOr<String?> redirect(BuildContext context, GoRouterState state) async {
if (AuthStorage.isLogin()) {
return Routes.homePage;
} else {
return null;
}
}
}

21
lib/core/middleware/language_middleware.dart

@ -0,0 +1,21 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:hadi_hoda_flutter/core/constants/my_constants.dart';
import 'package:hadi_hoda_flutter/core/routers/my_routes.dart';
import 'package:go_router/go_router.dart';
import 'package:hadi_hoda_flutter/core/utils/local_storage.dart';
class LanguageMiddleware {
static const LanguageMiddleware _i = LanguageMiddleware._internal();
const LanguageMiddleware._internal();
factory LanguageMiddleware() => _i;
static FutureOr<String?> redirect(BuildContext context, GoRouterState state) async {
if (LocalStorage.readData(key: MyConstants.selectLanguage).isNotEmpty) {
return Routes.homePage;
} else {
return null;
}
}
}

17
lib/core/params/language_params.dart

@ -0,0 +1,17 @@
class LanguageParams {
String? code;
LanguageParams({this.code});
LanguageParams copyWith({
String? code,
}) {
return LanguageParams(
code: code ?? this.code,
);
}
Map<String, dynamic> get toHeader => {
if (code != null) 'HTTP_X_USER_LANGUAGE': code,
};
}

13
lib/core/routers/my_routes.dart

@ -1,10 +1,13 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:hadi_hoda_flutter/core/middleware/language_middleware.dart';
import 'package:hadi_hoda_flutter/core/utils/context_provider.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_bloc.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/ui/home_page.dart';
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_bloc.dart';
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/intro_page.dart';
import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_bloc.dart';
import 'package:hadi_hoda_flutter/features/language/presentation/ui/language_page.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/ui/level_page.dart';
@ -19,6 +22,7 @@ class Routes {
factory Routes() => _i;
static const String introPage = '/intro_page';
static const String languagePage = '/language_page';
static const String homePage = '/home_page';
static const String questionPage = '/question_page';
static const String levelPage = '/level_page';
@ -36,6 +40,15 @@ GoRouter get appPages => GoRouter(
child: const IntroPage(),
),
),
GoRoute(
name: Routes.languagePage,
path: Routes.languagePage,
redirect: LanguageMiddleware.redirect,
builder: (context, state) => BlocProvider(
create: (context) => LanguageBloc(locator()),
child: const LanguagePage(),
),
),
GoRoute(
name: Routes.homePage,
path: Routes.homePage,

18
lib/core/widgets/button/enum/button_type.dart

@ -0,0 +1,18 @@
import 'dart:ui';
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
enum ButtonType {
type1,
type2;
static Map<ButtonType, String> get image => {
type1: MyAssets.button,
type2: MyAssets.button2,
};
static Map<ButtonType, Color> get textColor => {
type1: Color(0XFF1D6EFF),
type2: Color(0XFFD93D16),
};
}

50
lib/core/widgets/button/my_button.dart

@ -0,0 +1,50 @@
import 'package:flutter/material.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.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/theme/my_theme.dart';
import 'package:hadi_hoda_flutter/core/utils/my_image.dart';
import 'package:hadi_hoda_flutter/core/widgets/button/enum/button_type.dart';
class MyButton extends StatelessWidget {
const MyButton({
super.key,
this.onTap,
this.type = ButtonType.type1,
this.title,
});
final VoidCallback? onTap;
final ButtonType? type;
final String? title;
@override
Widget build(BuildContext context) {
return SizedBox(
height: 84,
width: 194,
child: InkWell(
onTap: onTap,
highlightColor: context.noColor,
splashColor: context.noColor,
child: Stack(
alignment: Alignment.center,
children: [
MyImage(
image: ButtonType.image[type] ?? MyAssets.button,
),
PositionedDirectional(
top: MySpaces.s2,
child: Text(
title ?? '',
style: DinoKids.regular45.copyWith(
color: ButtonType.textColor[type],
),
),
),
],
),
),
);
}
}

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

@ -76,7 +76,7 @@ class HomePage extends StatelessWidget {
),
InkWell(
child: MyImage(
image: MyAssets.start,
image: MyAssets.button,
size: checkSize(context: context, mobile: 90, tablet: 160),
),
onTap: () =>

2
lib/features/intro/presentation/bloc/intro_bloc.dart

@ -40,7 +40,7 @@ class IntroBloc extends Bloc<IntroEvent, IntroState> {
(data) async {
await Future.delayed(
Duration(milliseconds: 300), () {
ContextProvider.context!.goNamed(Routes.homePage);
ContextProvider.context!.goNamed(Routes.languagePage);
},
);
},

46
lib/features/language/data/datasource/language_datasource.dart

@ -0,0 +1,46 @@
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/network/http_request.dart';
import 'package:hadi_hoda_flutter/core/params/language_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/features/level/data/model/level_model.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/total_data_entity.dart';
import 'package:hive/hive.dart';
abstract class ILanguageDatasource {
Future<void> saveLevels({required LanguageParams params});
}
class LanguageDatasourceImpl implements ILanguageDatasource {
final IHttpRequest httpRequest;
const LanguageDatasourceImpl(this.httpRequest);
@override
Future<void> saveLevels({required LanguageParams params}) async {
await LocalStorage.saveData(
key: MyConstants.selectLanguage,
value: params.code ?? 'fa',
);
final Box<TotalDataEntity> data = Hive.box(MyConstants.levelBox);
final TotalDataEntity findData = data.values.singleWhere(
(e) => e.code == params.code,
orElse: () => TotalDataEntity(),
);
if (findData.code != params.code) {
final response = await httpRequest.get(
path: MyApi.levels,
header: params.toHeader,
);
final List<LevelEntity> levels = BaseResponse.getDataList<LevelEntity>(
response?['result'],
(json) => LevelModel.fromJson(json),
);
await data.add(TotalDataEntity(code: params.code, levels: levels));
}
}
}

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

@ -0,0 +1,29 @@
import 'package:hadi_hoda_flutter/core/params/language_params.dart';
import 'package:flutter/foundation.dart';
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart';
import 'package:hadi_hoda_flutter/core/params/no_params.dart';
import 'package:hadi_hoda_flutter/core/utils/data_state.dart';
import 'package:hadi_hoda_flutter/features/language/data/datasource/language_datasource.dart';
import 'package:hadi_hoda_flutter/features/language/domain/repository/language_repository.dart';
class LanguageRepositoryImpl implements ILanguageRepository {
final ILanguageDatasource datasource;
const LanguageRepositoryImpl(this.datasource);
@override
Future<DataState<NoParams, MyException>> saveLevels({required LanguageParams params}) async {
try {
await datasource.saveLevels(params: params);
return DataState.success(NoParams());
} on MyException catch (e) {
return DataState.error(e);
} catch (e) {
if (kDebugMode) {
rethrow;
} else {
return DataState.error(MyException(errorMessage: '$e'));
}
}
}
}

17
lib/features/language/domain/entity/language_entity.dart

@ -0,0 +1,17 @@
import 'package:equatable/equatable.dart';
class LanguageEntity extends Equatable {
final String? title;
final String? code;
const LanguageEntity({
this.title,
this.code,
});
@override
List<Object?> get props => [
title,
code,
];
}

8
lib/features/language/domain/repository/language_repository.dart

@ -0,0 +1,8 @@
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart';
import 'package:hadi_hoda_flutter/core/params/language_params.dart';
import 'package:hadi_hoda_flutter/core/params/no_params.dart';
import 'package:hadi_hoda_flutter/core/utils/data_state.dart';
abstract class ILanguageRepository {
Future<DataState<NoParams, MyException>> saveLevels({required LanguageParams params});
}

17
lib/features/language/domain/usecases/save_levels_usecase.dart

@ -0,0 +1,17 @@
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart';
import 'package:hadi_hoda_flutter/core/params/language_params.dart';
import 'package:hadi_hoda_flutter/core/params/no_params.dart';
import 'package:hadi_hoda_flutter/core/usecase/usecase.dart';
import 'package:hadi_hoda_flutter/core/utils/data_state.dart';
import 'package:hadi_hoda_flutter/features/language/domain/repository/language_repository.dart';
class SaveLevelsUseCase implements UseCase<NoParams, LanguageParams> {
final ILanguageRepository repository;
const SaveLevelsUseCase(this.repository);
@override
Future<DataState<NoParams, MyException>> call(LanguageParams params) {
return repository.saveLevels(params: params);
}
}

60
lib/features/language/presentation/bloc/language_bloc.dart

@ -0,0 +1,60 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:hadi_hoda_flutter/core/params/language_params.dart';
import 'package:hadi_hoda_flutter/core/routers/my_routes.dart';
import 'package:hadi_hoda_flutter/core/status/base_status.dart';
import 'package:hadi_hoda_flutter/core/utils/context_provider.dart';
import 'package:hadi_hoda_flutter/features/language/domain/entity/language_entity.dart';
import 'package:hadi_hoda_flutter/features/language/domain/usecases/save_levels_usecase.dart';
import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_event.dart';
import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_state.dart';
class LanguageBloc extends Bloc<LanguageEvent, LanguageState> {
/// ------------constructor------------
LanguageBloc(
this._saveLevelsUseCase,
) : super(const LanguageState()) {
on<ChangeLanguageEvent>(_changeLanguageEvent);
on<SaveLevelsEvent>(_saveLevelsEvent);
}
/// ------------UseCases------------
final SaveLevelsUseCase _saveLevelsUseCase;
/// ------------Variables------------
final List<LanguageEntity> languages = [
LanguageEntity(title: 'Persian (فارسی)', code: 'fa'),
LanguageEntity(title: 'English (English)', code: 'en'),
LanguageEntity(title: 'Arabic (عربی)', code: 'ar'),
];
/// ------------Controllers------------
/// ------------Functions------------
FutureOr<void> _changeLanguageEvent(
ChangeLanguageEvent event,
Emitter<LanguageState> emit,
) {
emit(state.copyWith(selectedLang: event.lang));
}
/// ------------Api Calls------------
FutureOr<void> _saveLevelsEvent(
SaveLevelsEvent event,
Emitter<LanguageState> emit,
) async {
emit(state.copyWith(saveLevelsStatus: const BaseLoading()));
await _saveLevelsUseCase(LanguageParams(code: state.selectedLang.code)).then(
(value) => value.fold(
(data) {
emit(state.copyWith(saveLevelsStatus: const BaseInit()));
ContextProvider.context!.goNamed(Routes.homePage);
},
(error) {
emit(state.copyWith(saveLevelsStatus: const BaseInit()));
},
),
);
}
}

16
lib/features/language/presentation/bloc/language_event.dart

@ -0,0 +1,16 @@
import 'package:hadi_hoda_flutter/features/language/domain/entity/language_entity.dart';
sealed class LanguageEvent {
const LanguageEvent();
}
class ChangeLanguageEvent extends LanguageEvent {
final LanguageEntity lang;
const ChangeLanguageEvent(this.lang);
}
class SaveLevelsEvent extends LanguageEvent {
const SaveLevelsEvent();
}

22
lib/features/language/presentation/bloc/language_state.dart

@ -0,0 +1,22 @@
import 'package:hadi_hoda_flutter/core/status/base_status.dart';
import 'package:hadi_hoda_flutter/features/language/domain/entity/language_entity.dart';
class LanguageState {
final BaseStatus saveLevelsStatus;
final LanguageEntity selectedLang;
const LanguageState({
this.saveLevelsStatus = const BaseInit(),
this.selectedLang = const LanguageEntity(code: 'fa'),
});
LanguageState copyWith({
BaseStatus? saveLevelsStatus,
LanguageEntity? selectedLang,
}) {
return LanguageState(
saveLevelsStatus: saveLevelsStatus ?? this.saveLevelsStatus,
selectedLang: selectedLang ?? this.selectedLang,
);
}
}

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

@ -0,0 +1,157 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
import 'package:hadi_hoda_flutter/common_ui/theme/my_theme.dart';
import 'package:hadi_hoda_flutter/core/status/base_status.dart';
import 'package:hadi_hoda_flutter/core/utils/my_image.dart';
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart';
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart';
import 'package:hadi_hoda_flutter/core/widgets/button/my_button.dart';
import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_bloc.dart';
import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_event.dart';
import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_state.dart';
class LanguagePage extends StatelessWidget {
const LanguagePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
height: context.heightScreen,
width: context.widthScreen,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0XFF00154C), Color(0XFF150532)],
),
image: DecorationImage(
image: AssetImage(MyAssets.pattern),
scale: 3,
repeat: ImageRepeat.repeat,
colorFilter: ColorFilter.mode(
Colors.white.withValues(alpha: 0.2),
BlendMode.srcIn,
),
),
),
child: Padding(
padding: EdgeInsets.only(
left: 60,
right: 60,
bottom: MediaQuery.viewPaddingOf(context).bottom + MySpaces.s16,
top: MediaQuery.viewPaddingOf(context).bottom + 50,
),
child: Column(
children: [_title(context), _list(context), _btn(context)],
),
),
),
);
}
Widget _title(BuildContext context) {
return Row(
spacing: MySpaces.s10,
mainAxisAlignment: MainAxisAlignment.center,
children: [
MyImage(image: MyAssets.lang, size: 28),
Text(
context.translate.select_language,
style: Marhey.semiBold22.copyWith(color: Color(0XFF847AC4)),
),
],
);
}
Expanded _list(BuildContext context) {
return Expanded(
child: Material(
color: context.noColor,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
context.read<LanguageBloc>().languages.length,
(index) => BlocBuilder<LanguageBloc, LanguageState>(
buildWhen: (previous, current) =>
previous.selectedLang.code != current.selectedLang.code,
builder: (context, state) {
final LanguageBloc languageBloc = context.read<LanguageBloc>();
return ListTile(
selected: state.selectedLang.code ==
languageBloc.languages[index].code,
onTap: () {
languageBloc.add(
ChangeLanguageEvent(languageBloc.languages[index]));
},
title: Text(context.read<LanguageBloc>().languages[index].title ?? ''),
titleTextStyle: Marhey.medium16.copyWith(
color: context.primaryColor,
),
contentPadding: EdgeInsets.symmetric(
vertical: MySpaces.s12,
horizontal: 30,
),
minVerticalPadding: 0,
minTileHeight: 0,
minLeadingWidth: 0,
horizontalTitleGap: MySpaces.s12,
trailing: BlocBuilder<LanguageBloc, LanguageState>(
builder: (context, state) {
if (state.saveLevelsStatus is BaseLoading && (state
.selectedLang.code == languageBloc.languages[index].code)) {
return CupertinoActivityIndicator(
color: context.primaryColor,
);
} else {
return SizedBox.shrink();
}
},
),
leading: state.selectedLang.code ==
languageBloc.languages[index].code ? Container(
height: 17,
width: 17,
padding: EdgeInsets.all(3),
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
width: 1,
color: Color(0XFF3CFF3C),
),
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Color(0XFF48D336),
Color(0XFF2D7C23),
],
),
),
child: MyImage(image: MyAssets.doneRounded),
) : SizedBox(height: 17, width: 17),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12)),
),
selectedTileColor: context.primaryColor.withValues(alpha: 0.2),
selectedColor: context.primaryColor,
);
}
),
),
),
),
);
}
Widget _btn(BuildContext context) {
return MyButton(
onTap: () => context.read<LanguageBloc>().add(SaveLevelsEvent()),
title: context.translate.select,
);
}
}

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

@ -1,10 +1,6 @@
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/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/response/base_response.dart';
import 'package:hadi_hoda_flutter/features/level/data/model/level_model.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
import 'package:hive/hive.dart';
@ -12,26 +8,6 @@ abstract class ILevelDatasource {
Future<List<LevelEntity>> getLevels({required LevelParams params});
}
/// Remote
class RemoteLevelDatasourceImpl implements ILevelDatasource {
final IHttpRequest httpRequest;
const RemoteLevelDatasourceImpl(this.httpRequest);
@override
Future<List<LevelEntity>> getLevels({required LevelParams params}) async {
final response = await httpRequest.get(
path: MyApi.baseUrl,
);
return BaseResponse.getDataList<LevelEntity>(
response?['data'],
(json) => LevelModel.fromJson(json),
);
}
}
/// Local
class LocalLevelDatasourceImpl implements ILevelDatasource {

2
lib/features/level/domain/entity/level_entity.dart

@ -3,7 +3,7 @@ import 'package:hive/hive.dart';
part 'level_entity.g.dart';
@HiveType(typeId: 0)
@HiveType(typeId: 1)
class LevelEntity extends HiveObject {
@HiveField(0)
int? id;

2
lib/features/level/domain/entity/level_entity.g.dart

@ -8,7 +8,7 @@ part of 'level_entity.dart';
class LevelEntityAdapter extends TypeAdapter<LevelEntity> {
@override
final int typeId = 0;
final int typeId = 1;
@override
LevelEntity read(BinaryReader reader) {

17
lib/features/level/domain/entity/total_data_entity.dart

@ -0,0 +1,17 @@
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
import 'package:hive/hive.dart';
part 'total_data_entity.g.dart';
@HiveType(typeId: 0)
class TotalDataEntity extends HiveObject{
@HiveField(0)
String? code;
@HiveField(1)
List<LevelEntity>? levels;
TotalDataEntity({
this.code,
this.levels,
});
}

44
lib/features/level/domain/entity/total_data_entity.g.dart

@ -0,0 +1,44 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'total_data_entity.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class TotalDataEntityAdapter extends TypeAdapter<TotalDataEntity> {
@override
final int typeId = 0;
@override
TotalDataEntity read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return TotalDataEntity(
code: fields[0] as String?,
levels: (fields[1] as List?)?.cast<LevelEntity>(),
);
}
@override
void write(BinaryWriter writer, TotalDataEntity obj) {
writer
..writeByte(2)
..writeByte(0)
..write(obj.code)
..writeByte(1)
..write(obj.levels);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is TotalDataEntityAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

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

@ -3,7 +3,7 @@ import 'package:hive/hive.dart';
part 'answer_entity.g.dart';
@HiveType(typeId: 2)
@HiveType(typeId: 3)
class AnswerEntity extends HiveObject {
@HiveField(0)
int? id;

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

@ -8,7 +8,7 @@ part of 'answer_entity.dart';
class AnswerEntityAdapter extends TypeAdapter<AnswerEntity> {
@override
final int typeId = 2;
final int typeId = 3;
@override
AnswerEntity read(BinaryReader reader) {

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

@ -2,7 +2,7 @@ import 'package:hive/hive.dart';
part 'file_entity.g.dart';
@HiveType(typeId: 3)
@HiveType(typeId: 4)
class FileEntity extends HiveObject {
@HiveField(0)
String? filename;

2
lib/features/question/domain/entity/file_entity.g.dart

@ -8,7 +8,7 @@ part of 'file_entity.dart';
class FileEntityAdapter extends TypeAdapter<FileEntity> {
@override
final int typeId = 3;
final int typeId = 4;
@override
FileEntity read(BinaryReader reader) {

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

@ -4,7 +4,7 @@ import 'package:hive/hive.dart';
part 'question_entity.g.dart';
@HiveType(typeId: 1)
@HiveType(typeId: 2)
class QuestionEntity extends HiveObject {
@HiveField(0)
int? id;

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

@ -8,7 +8,7 @@ part of 'question_entity.dart';
class QuestionEntityAdapter extends TypeAdapter<QuestionEntity> {
@override
final int typeId = 1;
final int typeId = 2;
@override
QuestionEntity read(BinaryReader reader) {

15
lib/init_bindings.dart

@ -12,9 +12,14 @@ import 'package:hadi_hoda_flutter/features/intro/data/repository_impl/intro_repo
import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/get_files_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/loading_stream_usecase.dart';
import 'package:hadi_hoda_flutter/features/language/data/datasource/language_datasource.dart';
import 'package:hadi_hoda_flutter/features/language/data/repository_impl/language_repository_impl.dart';
import 'package:hadi_hoda_flutter/features/language/domain/repository/language_repository.dart';
import 'package:hadi_hoda_flutter/features/language/domain/usecases/save_levels_usecase.dart';
import 'package:hadi_hoda_flutter/features/level/data/datasource/level_datasource.dart';
import 'package:hadi_hoda_flutter/features/level/data/repository_impl/level_repository_impl.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/total_data_entity.dart';
import 'package:hadi_hoda_flutter/features/level/domain/repository/level_repository.dart';
import 'package:hadi_hoda_flutter/features/level/domain/usecases/get_levels_usecase.dart';
import 'package:hadi_hoda_flutter/features/question/data/datasource/question_datasource.dart';
@ -49,6 +54,11 @@ void initBindings() {
locator.registerLazySingleton<GetFilesUseCase>(() => GetFilesUseCase(locator()));
locator.registerLazySingleton<LoadingStreamUseCase>(() => LoadingStreamUseCase(locator()));
/// Language Feature
locator.registerLazySingleton<ILanguageDatasource>(() => LanguageDatasourceImpl(locator()));
locator.registerLazySingleton<ILanguageRepository>(() => LanguageRepositoryImpl(locator()));
locator.registerLazySingleton<SaveLevelsUseCase>(() => SaveLevelsUseCase(locator()));
/// Home Feature
locator.registerLazySingleton<IHomeDatasource>(() => HomeDatasourceImpl(locator()));
locator.registerLazySingleton<IHomeRepository>(() => HomeRepositoryImpl(locator()));
@ -72,7 +82,8 @@ Future<void> initDataBase() async {
..registerAdapter<FileEntity>(FileEntityAdapter())
..registerAdapter<AnswerEntity>(AnswerEntityAdapter())
..registerAdapter<QuestionEntity>(QuestionEntityAdapter())
..registerAdapter<LevelEntity>(LevelEntityAdapter());
..registerAdapter<LevelEntity>(LevelEntityAdapter())
..registerAdapter<TotalDataEntity>(TotalDataEntityAdapter());
await Hive.openBox<LevelEntity>(MyConstants.levelBox);
await Hive.openBox<TotalDataEntity>(MyConstants.levelBox);
}

4
lib/l10n/app_en.arb

@ -1,5 +1,7 @@
{
"about_us": "About us",
"about_us_desc" : "Rive combines an interactive design tool, a new stateful graphics format, a lightweight multi-platform runtime, and a blazing-fast vector renderer. \nThis end-to-end pipeline brings interfaces to life with motion. It gives designers and devs the tools to build.",
"tap_to_select": "Tap the correct option to select."
"tap_to_select": "Tap the correct option to select.",
"select_language": "Select language",
"select": "Select"
}

12
lib/l10n/app_localizations.dart

@ -111,6 +111,18 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Tap the correct option to select.'**
String get tap_to_select;
/// No description provided for @select_language.
///
/// In en, this message translates to:
/// **'Select language'**
String get select_language;
/// No description provided for @select.
///
/// In en, this message translates to:
/// **'Select'**
String get select;
}
class _AppLocalizationsDelegate

6
lib/l10n/app_localizations_en.dart

@ -17,4 +17,10 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get tap_to_select => 'Tap the correct option to select.';
@override
String get select_language => 'Select language';
@override
String get select => 'Select';
}
Loading…
Cancel
Save