134 changed files with 3230 additions and 1967 deletions
-
8.gitignore
-
33.metadata
-
30README.md
-
13analysis_options.yaml
-
27android/app/build.gradle.kts
-
3android/app/src/main/AndroidManifest.xml
-
BINassets/images/gift.png
-
BINassets/images/gift_background.png
-
BINassets/images/gift_disable.png
-
10assets/svg/icon_play_video.svg
-
BINassets/videos/intro_1.mp4
-
BINassets/videos/intro_2.mp4
-
BINassets/videos/intro_3.mp4
-
BINassets/videos/intro_4.mp4
-
BINassets/videos/intro_5.mp4
-
4compile_svg.zsh
-
2ios/Podfile
-
25ios/Podfile.lock
-
5ios/Runner/Info.plist
-
69lib/common_ui/resources/my_assets.dart
-
11lib/common_ui/theme/my_theme.dart
-
2lib/core/constants/my_api.dart
-
4lib/core/constants/my_constants.dart
-
10lib/core/middlewares/my_middlewares.dart
-
4lib/core/routers/hero_dialog_route.dart
-
25lib/core/routers/my_routes.dart
-
12lib/core/services/audio_service.dart
-
32lib/core/utils/app_life_cycle.dart
-
11lib/core/utils/pre_cache_image.dart
-
4lib/core/utils/storage_path.dart
-
4lib/core/widgets/animations/fade_anim.dart
-
39lib/core/widgets/animations/fade_anim_controller.dart
-
59lib/core/widgets/animations/fade_anim_delayed.dart
-
38lib/core/widgets/animations/globe_animation.dart
-
2lib/core/widgets/animations/rotation_anim.dart
-
4lib/core/widgets/animations/scale_anim.dart
-
4lib/core/widgets/animations/ship_anim.dart
-
2lib/core/widgets/animations/slide_anim.dart
-
6lib/core/widgets/animations/slide_down_fade.dart
-
6lib/core/widgets/animations/slide_up_fade.dart
-
30lib/core/widgets/answer_box/answer_box.dart
-
6lib/core/widgets/answer_box/answer_box_show.dart
-
111lib/core/widgets/answer_box/answer_box_showcase.dart
-
118lib/core/widgets/answer_box/styles/image_box.dart
-
46lib/core/widgets/answer_box/styles/picture_box.dart
-
6lib/core/widgets/answer_box/styles/text_box.dart
-
2lib/core/widgets/button/my_blue_button.dart
-
2lib/core/widgets/button/my_white_button.dart
-
2lib/core/widgets/button/my_yellow_button.dart
-
60lib/core/widgets/confetti/my_confetti.dart
-
17lib/core/widgets/dialog/about_us_dialog.dart
-
25lib/core/widgets/dialog/exit_dialog.dart
-
8lib/core/widgets/dialog/hadith_dialog.dart
-
386lib/core/widgets/dialog/reward_dialog.dart
-
6lib/core/widgets/error/error_state.dart
-
5lib/core/widgets/pop_scope/my_pop_scope.dart
-
20lib/core/widgets/showcase/my_showcase_widget.dart
-
76lib/core/widgets/video/my_video_player.dart
-
22lib/features/download/data/datasource/download_datasource.dart
-
6lib/features/download/presentation/bloc/download_bloc.dart
-
8lib/features/download/presentation/ui/download_page.dart
-
10lib/features/download/presentation/ui/widgets/download_loading_widget.dart
-
33lib/features/guider/data/datasource/guider_datasource.dart
-
28lib/features/guider/data/repository_impl/guider_repository_impl.dart
-
7lib/features/guider/domain/repository/guider_repository.dart
-
17lib/features/guider/domain/usecases/get_first_level_usecase.dart
-
131lib/features/guider/presentation/bloc/guider_bloc.dart
-
8lib/features/guider/presentation/bloc/guider_event.dart
-
27lib/features/guider/presentation/bloc/guider_state.dart
-
248lib/features/guider/presentation/ui/guider_page.dart
-
54lib/features/home/presentation/bloc/home_bloc.dart
-
8lib/features/home/presentation/ui/home_page.dart
-
103lib/features/intro/presentation/bloc/intro_bloc.dart
-
4lib/features/intro/presentation/bloc/intro_event.dart
-
8lib/features/intro/presentation/ui/intro_page.dart
-
54lib/features/intro/presentation/ui/screens/intro_1_screen.dart
-
38lib/features/intro/presentation/ui/screens/intro_2_screen.dart
-
38lib/features/intro/presentation/ui/screens/intro_3_screen.dart
-
37lib/features/intro/presentation/ui/screens/intro_4_screen.dart
-
35lib/features/intro/presentation/ui/screens/intro_5_screen.dart
-
12lib/features/intro/presentation/ui/widgets/bubble_chat_widget.dart
-
4lib/features/language/presentation/bloc/language_bloc.dart
-
23lib/features/language/presentation/ui/language_page.dart
-
14lib/features/language/presentation/ui/widgets/language_widget.dart
-
10lib/features/level/data/datasource/level_datasource.dart
-
25lib/features/level/data/model/node_model.dart
-
21lib/features/level/data/model/prize_model.dart
-
6lib/features/level/data/repository_impl/level_repository_impl.dart
-
30lib/features/level/domain/entity/node_entity.dart
-
86lib/features/level/domain/entity/node_entity.g.dart
-
25lib/features/level/domain/entity/prize_entity.dart
-
53lib/features/level/domain/entity/prize_entity.g.dart
-
6lib/features/level/domain/entity/total_data_entity.dart
-
4lib/features/level/domain/entity/total_data_entity.g.dart
-
6lib/features/level/domain/repository/level_repository.dart
-
6lib/features/level/domain/usecases/get_levels_usecase.dart
-
31lib/features/level/presentation/bloc/level_bloc.dart
-
2lib/features/level/presentation/bloc/level_event.dart
-
26lib/features/level/presentation/ui/level_page.dart
-
2lib/features/level/presentation/ui/widgets/bottom_path.dart
@ -1,33 +0,0 @@ |
|||||
# This file tracks properties of this Flutter project. |
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc. |
|
||||
# |
|
||||
# This file should be version controlled and should not be manually edited. |
|
||||
|
|
||||
version: |
|
||||
revision: "d693b4b9dbac2acd4477aea4555ca6dcbea44ba2" |
|
||||
channel: "stable" |
|
||||
|
|
||||
project_type: app |
|
||||
|
|
||||
# Tracks metadata for the flutter migrate command |
|
||||
migration: |
|
||||
platforms: |
|
||||
- platform: root |
|
||||
create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 |
|
||||
base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 |
|
||||
- platform: android |
|
||||
create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 |
|
||||
base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 |
|
||||
- platform: ios |
|
||||
create_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 |
|
||||
base_revision: d693b4b9dbac2acd4477aea4555ca6dcbea44ba2 |
|
||||
|
|
||||
# User provided section |
|
||||
|
|
||||
# List of Local paths (relative to this file) that should be |
|
||||
# ignored by the migrate tool. |
|
||||
# |
|
||||
# Files that are not part of the templates will be ignored by default. |
|
||||
unmanaged_files: |
|
||||
- 'lib/main.dart' |
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj' |
|
||||
@ -1,3 +1,33 @@ |
|||||
# hadi_hoda_flutter |
# hadi_hoda_flutter |
||||
|
|
||||
A new Flutter project. |
A new Flutter project. |
||||
|
|
||||
|
## Building for Google Play Release |
||||
|
|
||||
|
This project includes a keystore for signing release builds for Google Play Store. |
||||
|
|
||||
|
### Security Setup |
||||
|
- **Keystore file**: `android/app/upload-keystore.jks` (excluded from git) |
||||
|
- **Properties file**: `android/keystore.properties` (excluded from git) |
||||
|
- **Key alias**: `upload` |
||||
|
|
||||
|
### Important Security Notes |
||||
|
- 🔐 Keystore files and passwords are excluded from version control |
||||
|
- 🔐 Keep `upload-keystore.jks` and `keystore.properties` files secure |
||||
|
- 🔐 Backup these files in a safe location separate from your code |
||||
|
- 🔐 Never share passwords or keystore files with unauthorized parties |
||||
|
|
||||
|
### Building Release APK |
||||
|
|
||||
|
To build a release APK for Google Play: |
||||
|
|
||||
|
```bash |
||||
|
flutter build apk --release |
||||
|
``` |
||||
|
|
||||
|
The APK will be generated in `build/app/outputs/flutter-apk/` with the name `Hadi & Hoda v{version}+{versionCode}.apk`. |
||||
|
|
||||
|
### Important Security Notes |
||||
|
- Keep the keystore file (`upload-keystore.jks`) secure and don't commit it to version control |
||||
|
- Backup the keystore file in a safe location |
||||
|
- Never share the passwords with unauthorized parties |
||||
@ -1 +1,14 @@ |
|||||
include: package:flutter_lints/flutter.yaml |
include: package:flutter_lints/flutter.yaml |
||||
|
|
||||
|
linter: |
||||
|
rules: |
||||
|
prefer_const_constructors: true |
||||
|
prefer_const_declarations: true |
||||
|
prefer_const_constructors_in_immutables: true |
||||
|
prefer_final_fields: true |
||||
|
prefer_final_locals: true |
||||
|
avoid_classes_with_only_static_members: true |
||||
|
avoid_print: true |
||||
|
always_declare_return_types: true |
||||
|
use_key_in_widget_constructors: true |
||||
|
require_trailing_commas: true |
||||
|
After Width: 71 | Height: 75 | Size: 4.3 KiB |
|
After Width: 101 | Height: 105 | Size: 12 KiB |
|
After Width: 95 | Height: 100 | Size: 5.3 KiB |
@ -0,0 +1,10 @@ |
|||||
|
<svg width="62" height="62" viewBox="0 0 62 62" fill="none" xmlns="http://www.w3.org/2000/svg"> |
||||
|
<rect x="22.1758" y="19.1177" width="22.1765" height="24.4706" fill="white"/> |
||||
|
<path d="M30.5879 6.61768C36.9451 6.61771 43.0418 9.14309 47.5371 13.6382C52.0325 18.1335 54.5586 24.231 54.5586 30.5884C54.5586 36.9457 52.0324 43.0423 47.5371 47.5376C43.0418 52.0329 36.9452 54.5591 30.5879 54.5591C24.2305 54.5591 18.1331 52.033 13.6377 47.5376C9.1426 43.0423 6.61722 36.9456 6.61719 30.5884C6.61719 24.231 9.14234 18.1335 13.6377 13.6382C18.1331 9.14283 24.2305 6.61768 30.5879 6.61768ZM27.7012 20.9155C27.0578 20.8844 26.4176 21.0286 25.8496 21.3325C25.2817 21.6365 24.8066 22.0886 24.4756 22.6411C24.1446 23.1937 23.9697 23.8261 23.9697 24.4702V36.7056C23.9696 37.3497 24.1447 37.9821 24.4756 38.5347C24.8066 39.0873 25.2817 39.5403 25.8496 39.8442C26.4176 40.1481 27.0578 40.2924 27.7012 40.2612C28.3447 40.23 28.9679 40.0241 29.5039 39.6665L38.6807 33.5493C39.168 33.2243 39.5674 32.7836 39.8438 32.2671C40.1201 31.7506 40.2646 31.1741 40.2646 30.5884C40.2646 30.0026 40.1202 29.4252 39.8438 28.9087C39.5674 28.3924 39.1679 27.9523 38.6807 27.6274L29.5039 21.5093C28.968 21.1518 28.3446 20.9468 27.7012 20.9155Z" fill="#EB511A" stroke="url(#paint0_linear_42_2463)"/> |
||||
|
<defs> |
||||
|
<linearGradient id="paint0_linear_42_2463" x1="30.5878" y1="6.11768" x2="30.5878" y2="55.0589" gradientUnits="userSpaceOnUse"> |
||||
|
<stop stop-color="#FFCB32"/> |
||||
|
<stop offset="1" stop-color="#FFA105"/> |
||||
|
</linearGradient> |
||||
|
</defs> |
||||
|
</svg> |
||||
@ -0,0 +1,4 @@ |
|||||
|
for f in assets/svg/*.svg; do |
||||
|
echo "Compiling $f" |
||||
|
fvm dart run vector_graphics_compiler -i "$f" -o "${f}.vec" |
||||
|
done |
||||
@ -1,10 +1,17 @@ |
|||||
import 'package:flutter/material.dart'; |
import 'package:flutter/material.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; |
||||
|
|
||||
class MyTheme { |
class MyTheme { |
||||
static const MyTheme _i = MyTheme._internal(); |
static const MyTheme _i = MyTheme._internal(); |
||||
const MyTheme._internal(); |
const MyTheme._internal(); |
||||
factory MyTheme() => _i; |
factory MyTheme() => _i; |
||||
|
|
||||
static final ThemeData light = ThemeData(brightness: Brightness.light); |
|
||||
static final ThemeData dark = ThemeData(brightness: Brightness.dark); |
|
||||
|
static final ThemeData light = ThemeData( |
||||
|
brightness: Brightness.light, |
||||
|
scaffoldBackgroundColor: MyColors.purple, |
||||
|
); |
||||
|
static final ThemeData dark = ThemeData( |
||||
|
brightness: Brightness.dark, |
||||
|
scaffoldBackgroundColor: MyColors.purple, |
||||
|
); |
||||
} |
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
import 'package:flutter/material.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/init_bindings.dart'; |
||||
|
|
||||
|
class AppLifeCycleController extends WidgetsBindingObserver { |
||||
|
AppLifeCycleController() { |
||||
|
WidgetsBinding.instance.addObserver(this); |
||||
|
} |
||||
|
|
||||
|
void dispose() { |
||||
|
WidgetsBinding.instance.removeObserver(this); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
void didChangeAppLifecycleState(AppLifecycleState state) { |
||||
|
final AudioService mainAudio = locator( |
||||
|
instanceName: MyConstants.mainAudioService, |
||||
|
); |
||||
|
final AudioService effect = locator( |
||||
|
instanceName: MyConstants.effectAudioService, |
||||
|
); |
||||
|
|
||||
|
if (state == AppLifecycleState.paused || state == AppLifecycleState.inactive) { |
||||
|
mainAudio.pause(); |
||||
|
effect.pause(); |
||||
|
} else if (state == AppLifecycleState.resumed) { |
||||
|
mainAudio.play(); |
||||
|
effect.play(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/my_context.dart'; |
||||
|
|
||||
|
Future<void> preCacheImages() async { |
||||
|
await Future.wait( |
||||
|
MyAssets.images.map( |
||||
|
(assetPath) => precacheImage(AssetImage(assetPath), MyContext.get), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
import 'package:flutter/material.dart'; |
||||
|
|
||||
|
class FadeAnimController extends StatefulWidget { |
||||
|
const FadeAnimController({ |
||||
|
super.key, |
||||
|
required this.child, |
||||
|
required this.controller, |
||||
|
}); |
||||
|
|
||||
|
final Widget child; |
||||
|
final AnimationController controller; |
||||
|
|
||||
|
@override |
||||
|
State<FadeAnimController> createState() => _FadeAnimControllerState(); |
||||
|
} |
||||
|
|
||||
|
class _FadeAnimControllerState extends State<FadeAnimController> |
||||
|
with SingleTickerProviderStateMixin { |
||||
|
late Animation<double> _animation; |
||||
|
|
||||
|
@override |
||||
|
void initState() { |
||||
|
super.initState(); |
||||
|
_animation = Tween<double>( |
||||
|
begin: 0, |
||||
|
end: 1, |
||||
|
).animate(CurvedAnimation(parent: widget.controller, curve: Curves.linear)); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return AnimatedBuilder( |
||||
|
animation: widget.controller, |
||||
|
child: widget.child, |
||||
|
builder: (context, child) => |
||||
|
FadeTransition(opacity: _animation, child: child), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,59 @@ |
|||||
|
import 'package:flutter/material.dart'; |
||||
|
|
||||
|
class FadeAnimDelayed extends StatefulWidget { |
||||
|
const FadeAnimDelayed({ |
||||
|
super.key, |
||||
|
required this.child, |
||||
|
required this.duration, |
||||
|
}); |
||||
|
|
||||
|
final Widget child; |
||||
|
final Duration duration; |
||||
|
|
||||
|
@override |
||||
|
State<FadeAnimDelayed> createState() => _FadeAnimDelayedState(); |
||||
|
} |
||||
|
|
||||
|
class _FadeAnimDelayedState extends State<FadeAnimDelayed> |
||||
|
with SingleTickerProviderStateMixin { |
||||
|
late AnimationController _controller; |
||||
|
late Animation<double> _animation; |
||||
|
|
||||
|
@override |
||||
|
void initState() { |
||||
|
super.initState(); |
||||
|
_controller = AnimationController( |
||||
|
vsync: this, |
||||
|
duration: const Duration(milliseconds: 500), |
||||
|
reverseDuration: const Duration(seconds: 500), |
||||
|
); |
||||
|
_animation = Tween<double>( |
||||
|
begin: 0, |
||||
|
end: 1, |
||||
|
).animate(CurvedAnimation(parent: _controller, curve: Curves.linear)); |
||||
|
|
||||
|
startAnim(); |
||||
|
} |
||||
|
|
||||
|
Future<void> startAnim() async { |
||||
|
await Future.delayed(widget.duration, () { |
||||
|
_controller.forward(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
void dispose() { |
||||
|
_controller.dispose(); |
||||
|
super.dispose(); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return AnimatedBuilder( |
||||
|
animation: _controller, |
||||
|
child: widget.child, |
||||
|
builder: (context, child) => |
||||
|
FadeTransition(opacity: _animation, child: child), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,111 @@ |
|||||
|
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/core/utils/my_localization.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/answer_box/styles/picture_box.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/answer_box/styles/text_box.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/showcase/my_showcase_widget.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart'; |
||||
|
|
||||
|
class AnswerBoxShowCase extends StatefulWidget { |
||||
|
const AnswerBoxShowCase({ |
||||
|
super.key, |
||||
|
required this.answer, |
||||
|
required this.correctAnswer, |
||||
|
required this.index, |
||||
|
this.globalKey, |
||||
|
this.answerGlobalKey, |
||||
|
this.onTap, |
||||
|
this.onNotifTap, |
||||
|
}); |
||||
|
|
||||
|
final AnswerEntity answer; |
||||
|
final int correctAnswer; |
||||
|
final void Function(bool isCorrect, int correctAnswer)? onTap; |
||||
|
final int index; |
||||
|
final Function(AnswerEntity answer)? onNotifTap; |
||||
|
final GlobalKey? globalKey; |
||||
|
final GlobalKey? answerGlobalKey; |
||||
|
|
||||
|
@override |
||||
|
State<AnswerBoxShowCase> createState() => _AnswerBoxShowCaseState(); |
||||
|
} |
||||
|
|
||||
|
class _AnswerBoxShowCaseState extends State<AnswerBoxShowCase> { |
||||
|
bool selected = false; |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return Hero( |
||||
|
tag: 'Hero_answer_${widget.answer.id}', |
||||
|
child: Material( |
||||
|
type: MaterialType.transparency, |
||||
|
child: GestureDetector( |
||||
|
onTap: !selected |
||||
|
? () { |
||||
|
setState(() { |
||||
|
selected = true; |
||||
|
}); |
||||
|
widget.onTap?.call( |
||||
|
widget.index == widget.correctAnswer, |
||||
|
widget.correctAnswer, |
||||
|
); |
||||
|
} |
||||
|
: null, |
||||
|
child: Stack( |
||||
|
alignment: Alignment.topCenter, |
||||
|
children: [ |
||||
|
MyShowcaseWidget( |
||||
|
globalKey: widget.answerGlobalKey, |
||||
|
description: context.translate.showcase_answer, |
||||
|
child: AnswerPictureBox( |
||||
|
selected: selected, |
||||
|
index: widget.index, |
||||
|
image: widget.answer.image ?? '', |
||||
|
correctAnswer: widget.correctAnswer, |
||||
|
onTap: () { |
||||
|
widget.onNotifTap?.call(widget.answer); |
||||
|
}, |
||||
|
), |
||||
|
), |
||||
|
Positioned( |
||||
|
left: 0, |
||||
|
right: 0, |
||||
|
bottom: 0, |
||||
|
child: AnswerTextBox(text: widget.answer.title ?? ''), |
||||
|
), |
||||
|
PositionedDirectional( |
||||
|
top: setSize( |
||||
|
context: context, |
||||
|
mobile: MySpaces.s12, |
||||
|
tablet: MySpaces.s20, |
||||
|
), |
||||
|
end: setSize( |
||||
|
context: context, |
||||
|
mobile: MySpaces.s8, |
||||
|
tablet: MySpaces.s20, |
||||
|
), |
||||
|
child: MyShowcaseWidget( |
||||
|
globalKey: widget.globalKey, |
||||
|
type: ShowcaseTooltipType.bottom, |
||||
|
description: context.translate.showcase_notif, |
||||
|
child: GestureDetector( |
||||
|
onTap: () { |
||||
|
widget.onNotifTap?.call(widget.answer); |
||||
|
}, |
||||
|
child: MyImage( |
||||
|
image: MyAssets.iconNotif, |
||||
|
size: setSize(context: context, tablet: 50), |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,118 @@ |
|||||
|
import 'dart:io'; |
||||
|
|
||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; |
||||
|
|
||||
|
class ImageBox extends StatelessWidget { |
||||
|
const ImageBox({ |
||||
|
super.key, |
||||
|
required this.image, |
||||
|
}); |
||||
|
|
||||
|
final String image; |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return CustomPaint( |
||||
|
painter: _CustomShapePainter(), |
||||
|
child: ClipPath( |
||||
|
clipper: _CustomShapeClipper(), |
||||
|
child: Stack( |
||||
|
alignment: Alignment.center, |
||||
|
children: [ |
||||
|
AspectRatio( |
||||
|
aspectRatio: 1, |
||||
|
child: AnimatedSwitcher( |
||||
|
duration: const Duration(milliseconds: 150), |
||||
|
reverseDuration: const Duration(milliseconds: 150), |
||||
|
switchInCurve: Curves.linear, |
||||
|
switchOutCurve: Curves.linear, |
||||
|
child: Image( |
||||
|
width: context.widthScreen, |
||||
|
height: context.heightScreen, |
||||
|
image: FileImage(File(image)), |
||||
|
fit: BoxFit.cover, |
||||
|
), |
||||
|
transitionBuilder: (child, animation) => |
||||
|
FadeTransition( |
||||
|
opacity: animation, |
||||
|
child: child, |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class _CustomShapePainter extends CustomPainter { |
||||
|
@override |
||||
|
void paint(Canvas canvas, Size size) { |
||||
|
final Paint strokePaint = Paint() |
||||
|
..color = const Color(0XFFF2F7FF) |
||||
|
..style = PaintingStyle.stroke |
||||
|
..strokeWidth = 4; |
||||
|
|
||||
|
final Path path = _CustomShapeClipper().getClip(size); |
||||
|
canvas.drawPath(path, strokePaint); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool shouldRepaint(covariant CustomPainter oldDelegate) { |
||||
|
return this != oldDelegate; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class _CustomShapeClipper extends CustomClipper<Path> { |
||||
|
@override |
||||
|
Path getClip(Size size) { |
||||
|
// Original SVG dimensions to calculate the scaling factors. |
||||
|
const double originalWidth = 193.0; |
||||
|
const double originalHeight = 189.0; |
||||
|
|
||||
|
// Scaling factors to make the path responsive. |
||||
|
final double scaleX = size.width / originalWidth; |
||||
|
final double scaleY = size.height / originalHeight; |
||||
|
|
||||
|
// The path is defined using the scaled coordinates from the SVG. |
||||
|
final Path path = Path() |
||||
|
..moveTo(148.483 * scaleX, 4.10254 * scaleY) |
||||
|
..cubicTo(131.624 * scaleX, 1.93333 * scaleY, 111.221 * scaleX, 1.00169 * scaleY, 91.2451 * scaleX, 1.2666 * scaleY) |
||||
|
..cubicTo(71.2667 * scaleX, 1.53156 * scaleY, 51.7626 * scaleX, 2.99274 * scaleY, 36.6973 * scaleX, 5.59668 * scaleY) |
||||
|
..cubicTo(29.1597 * scaleX, 6.8995 * scaleY, 22.7796 * scaleX, 8.48114 * scaleY, 18.0205 * scaleX, 10.3203 * scaleY) |
||||
|
..cubicTo(15.641 * scaleX, 11.2399 * scaleY, 13.7026 * scaleX, 12.2101 * scaleY, 12.2383 * scaleX, 13.2188 * scaleY) |
||||
|
..cubicTo(10.7653 * scaleX, 14.2333 * scaleY, 9.84633 * scaleX, 15.2359 * scaleY, 9.3916 * scaleX, 16.1904 * scaleY) |
||||
|
..cubicTo(8.252 * scaleX, 18.5828 * scaleY, 7.18153 * scaleX, 22.466 * scaleY, 6.2207 * scaleX, 27.5654 * scaleY) |
||||
|
..cubicTo(5.26481 * scaleX, 32.6387 * scaleY, 4.43215 * scaleX, 38.8273 * scaleY, 3.73535 * scaleX, 45.7744 * scaleY) |
||||
|
..cubicTo(2.34189 * scaleX, 59.6675 * scaleY, 1.49647 * scaleX, 76.5363 * scaleY, 1.27832 * scaleX, 93.4678 * scaleY) |
||||
|
..cubicTo(1.06017 * scaleX, 110.4 * scaleY, 1.47057 * scaleX, 127.372 * scaleY, 2.58301 * scaleX, 141.473 * scaleY) |
||||
|
..cubicTo(3.13928 * scaleX, 148.524 * scaleY, 3.86921 * scaleX, 154.841 * scaleY, 4.78125 * scaleX, 160.068 * scaleY) |
||||
|
..cubicTo(5.69748 * scaleX, 165.32 * scaleY, 6.78334 * scaleX, 169.385 * scaleY, 8.01367 * scaleX, 171.984 * scaleY) |
||||
|
..cubicTo(8.53417 * scaleX, 173.084 * scaleY, 9.59654 * scaleX, 174.216 * scaleY, 11.2891 * scaleX, 175.343 * scaleY) |
||||
|
..cubicTo(12.9722 * scaleX, 176.463 * scaleY, 15.1988 * scaleX, 177.524 * scaleY, 17.9219 * scaleX, 178.515 * scaleY) |
||||
|
..cubicTo(23.3679 * scaleX, 180.496 * scaleY, 30.6491 * scaleX, 182.138 * scaleY, 39.1807 * scaleX, 183.437 * scaleY) |
||||
|
..cubicTo(56.2336 * scaleX, 186.032 * scaleY, 78.0934 * scaleX, 187.222 * scaleY, 99.8242 * scaleX, 187.064 * scaleY) |
||||
|
..cubicTo(121.556 * scaleX, 186.906 * scaleY, 143.101 * scaleX, 185.4 * scaleY, 159.525 * scaleX, 182.622 * scaleY) |
||||
|
..cubicTo(167.745 * scaleX, 181.232 * scaleY, 174.627 * scaleX, 179.531 * scaleY, 179.594 * scaleX, 177.548 * scaleY) |
||||
|
..cubicTo(182.079 * scaleX, 176.556 * scaleY, 184.034 * scaleX, 175.512 * scaleY, 185.429 * scaleX, 174.437 * scaleY) |
||||
|
..cubicTo(186.83 * scaleX, 173.355 * scaleY, 187.568 * scaleX, 172.319 * scaleY, 187.812 * scaleX, 171.361 * scaleY) |
||||
|
..lineTo(187.812 * scaleX, 171.361 * scaleY) // In SVG, this was H (horizontal line), equivalent to lineTo in Flutter |
||||
|
..cubicTo(189.156 * scaleX, 166.074 * scaleY, 190.148 * scaleX, 155.525 * scaleY, 190.773 * scaleX, 142.157 * scaleY) |
||||
|
..cubicTo(191.396 * scaleX, 128.832 * scaleY, 191.651 * scaleX, 112.822 * scaleY, 191.552 * scaleX, 96.6875 * scaleY) |
||||
|
..cubicTo(191.453 * scaleX, 80.5539 * scaleY, 191.001 * scaleX, 64.3091 * scaleY, 190.213 * scaleX, 50.5156 * scaleY) |
||||
|
..cubicTo(189.423 * scaleX, 36.6928 * scaleY, 188.299 * scaleX, 25.4153 * scaleY, 186.876 * scaleX, 19.167 * scaleY) |
||||
|
..cubicTo(186.404 * scaleX, 17.0929 * scaleY, 185.566 * scaleX, 15.3424 * scaleY, 184.087 * scaleX, 14.1582 * scaleY) |
||||
|
..cubicTo(181.343 * scaleX, 11.9613 * scaleY, 176.72 * scaleX, 9.98089 * scaleY, 170.561 * scaleX, 8.27539 * scaleY) |
||||
|
..cubicTo(164.434 * scaleX, 6.579 * scaleY, 156.914 * scaleX, 5.18731 * scaleY, 148.483 * scaleX, 4.10254 * scaleY) |
||||
|
..close(); // Closes the path to form a complete shape. |
||||
|
|
||||
|
return path; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool shouldReclip(covariant CustomClipper<Path> oldClipper) { |
||||
|
return this != oldClipper; |
||||
|
} |
||||
|
} |
||||
@ -1,60 +0,0 @@ |
|||||
import 'dart:math'; |
|
||||
|
|
||||
import 'package:confetti/confetti.dart'; |
|
||||
import 'package:flutter/material.dart'; |
|
||||
|
|
||||
class MyConfetti extends StatelessWidget { |
|
||||
const MyConfetti({super.key, required this.controller}); |
|
||||
|
|
||||
final ConfettiController controller; |
|
||||
|
|
||||
@override |
|
||||
Widget build(BuildContext context) { |
|
||||
return ConfettiWidget( |
|
||||
confettiController: controller, |
|
||||
shouldLoop: false, |
|
||||
blastDirectionality: BlastDirectionality.explosive, |
|
||||
blastDirection: pi / 2, |
|
||||
gravity: 1, |
|
||||
emissionFrequency: 0.5, |
|
||||
numberOfParticles: 15, |
|
||||
particleDrag: 0.1, |
|
||||
colors: const [ |
|
||||
Colors.red, |
|
||||
Colors.orange, |
|
||||
Colors.yellow, |
|
||||
Colors.green, |
|
||||
Colors.blue, |
|
||||
Colors.purple, |
|
||||
Colors.pink, |
|
||||
Colors.cyan, |
|
||||
], |
|
||||
createParticlePath: (size) { |
|
||||
double degToRad(double deg) => deg * (pi / 180.0); |
|
||||
|
|
||||
const numberOfPoints = 5; |
|
||||
final halfWidth = size.width / 2; |
|
||||
final externalRadius = halfWidth; |
|
||||
final internalRadius = halfWidth / 2.5; |
|
||||
final degreesPerStep = degToRad(360 / numberOfPoints); |
|
||||
final halfDegreesPerStep = degreesPerStep / 2; |
|
||||
final path = Path(); |
|
||||
final fullAngle = degToRad(360); |
|
||||
path.moveTo(size.width, halfWidth); |
|
||||
|
|
||||
for (double step = 0; step < fullAngle; step += degreesPerStep) { |
|
||||
path.lineTo( |
|
||||
halfWidth + externalRadius * cos(step), |
|
||||
halfWidth + externalRadius * sin(step), |
|
||||
); |
|
||||
path.lineTo( |
|
||||
halfWidth + internalRadius * cos(step + halfDegreesPerStep), |
|
||||
halfWidth + internalRadius * sin(step + halfDegreesPerStep), |
|
||||
); |
|
||||
} |
|
||||
path.close(); |
|
||||
return path; |
|
||||
}, |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,386 @@ |
|||||
|
import 'dart:ui'; |
||||
|
|
||||
|
import 'package:flutter/material.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_assets.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/core/routers/my_routes.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/set_platform_size.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/dialog/styles/dialog_background.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/level/domain/entity/prize_entity.dart'; |
||||
|
import 'package:lottie/lottie.dart'; |
||||
|
|
||||
|
Future<void> showRewardDialog({ |
||||
|
required BuildContext context, |
||||
|
required PrizeEntity prize, |
||||
|
}) async { |
||||
|
await showDialog( |
||||
|
context: context, |
||||
|
builder: (context) => RewardDialog(prize: prize), |
||||
|
barrierColor: MyColors.purple.withValues(alpha: 0.82), |
||||
|
useSafeArea: false, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
class RewardDialog extends StatelessWidget { |
||||
|
const RewardDialog({super.key, required this.prize}); |
||||
|
|
||||
|
final PrizeEntity prize; |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return Scaffold( |
||||
|
backgroundColor: MyColors.transparent, |
||||
|
body: BackdropFilter( |
||||
|
filter: ImageFilter.blur(sigmaX: 6, sigmaY: 6), |
||||
|
child: Center( |
||||
|
child: Padding( |
||||
|
padding: EdgeInsets.symmetric( |
||||
|
horizontal: |
||||
|
setSize(context: context, mobile: 18, tablet: 120) ?? 0, |
||||
|
), |
||||
|
child: Stack( |
||||
|
alignment: Alignment.center, |
||||
|
children: [ |
||||
|
Lottie.asset( |
||||
|
MyAnimations.confetti, |
||||
|
height: context.heightScreen, |
||||
|
fit: BoxFit.cover, |
||||
|
), |
||||
|
Stack( |
||||
|
clipBehavior: Clip.none, |
||||
|
children: [ |
||||
|
DialogBackground( |
||||
|
child: Column( |
||||
|
spacing: 34, |
||||
|
children: [ |
||||
|
Text( |
||||
|
context.translate.reward, |
||||
|
style: MYTextStyle.titr0.copyWith( |
||||
|
color: const Color(0XFF322386), |
||||
|
fontSize: 22, |
||||
|
), |
||||
|
), |
||||
|
Text( |
||||
|
prize.title ?? '', |
||||
|
style: MYTextStyle.titr0.copyWith( |
||||
|
color: const Color(0XFF322386), |
||||
|
fontSize: 22, |
||||
|
), |
||||
|
), |
||||
|
CustomPaint( |
||||
|
painter: _CustomShapePainter(), |
||||
|
child: ClipPath( |
||||
|
clipper: _CustomShapeClipper(), |
||||
|
child: GestureDetector( |
||||
|
onTap: () { |
||||
|
context.pushNamed( |
||||
|
Routes.videoPage, |
||||
|
extra: prize.animationURL as String, |
||||
|
); |
||||
|
}, |
||||
|
child: Stack( |
||||
|
alignment: Alignment.center, |
||||
|
children: [ |
||||
|
Image.network( |
||||
|
prize.imageURL ?? '', |
||||
|
loadingBuilder: (context, child, loadingProgress) => SizedBox( |
||||
|
height: 300, |
||||
|
width: 300, |
||||
|
child: child, |
||||
|
), |
||||
|
fit: BoxFit.cover, |
||||
|
), |
||||
|
const MyImage( |
||||
|
image: MyAssets.iconPlayVideo, |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
PositionedDirectional( |
||||
|
end: setSize(context: context, mobile: 30, tablet: 40), |
||||
|
top: -12, |
||||
|
child: GestureDetector( |
||||
|
onTap: context.pop, |
||||
|
behavior: HitTestBehavior.opaque, |
||||
|
child: MyImage( |
||||
|
image: MyAssets.closeBtn, |
||||
|
size: setSize(context: context, mobile: 40, tablet: 60), |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class _CustomShapePainter extends CustomPainter { |
||||
|
@override |
||||
|
void paint(Canvas canvas, Size size) { |
||||
|
final Paint strokePaint = Paint() |
||||
|
..color = const Color(0XFFF2F7FF) |
||||
|
..style = PaintingStyle.stroke |
||||
|
..strokeWidth = 4; |
||||
|
|
||||
|
final Path path = _CustomShapeClipper().getClip(size); |
||||
|
canvas.drawPath(path, strokePaint); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool shouldRepaint(covariant CustomPainter oldDelegate) { |
||||
|
return this != oldDelegate; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
class _CustomShapeClipper extends CustomClipper<Path> { |
||||
|
@override |
||||
|
Path getClip(Size size) { |
||||
|
// Original SVG dimensions to calculate the scaling factors. |
||||
|
const double originalWidth = 193.0; |
||||
|
const double originalHeight = 189.0; |
||||
|
|
||||
|
// Scaling factors to make the path responsive. |
||||
|
final double scaleX = size.width / originalWidth; |
||||
|
final double scaleY = size.height / originalHeight; |
||||
|
|
||||
|
// The path is defined using the scaled coordinates from the SVG. |
||||
|
final Path path = Path() |
||||
|
..moveTo(148.483 * scaleX, 4.10254 * scaleY) |
||||
|
..cubicTo( |
||||
|
131.624 * scaleX, |
||||
|
1.93333 * scaleY, |
||||
|
111.221 * scaleX, |
||||
|
1.00169 * scaleY, |
||||
|
91.2451 * scaleX, |
||||
|
1.2666 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
71.2667 * scaleX, |
||||
|
1.53156 * scaleY, |
||||
|
51.7626 * scaleX, |
||||
|
2.99274 * scaleY, |
||||
|
36.6973 * scaleX, |
||||
|
5.59668 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
29.1597 * scaleX, |
||||
|
6.8995 * scaleY, |
||||
|
22.7796 * scaleX, |
||||
|
8.48114 * scaleY, |
||||
|
18.0205 * scaleX, |
||||
|
10.3203 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
15.641 * scaleX, |
||||
|
11.2399 * scaleY, |
||||
|
13.7026 * scaleX, |
||||
|
12.2101 * scaleY, |
||||
|
12.2383 * scaleX, |
||||
|
13.2188 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
10.7653 * scaleX, |
||||
|
14.2333 * scaleY, |
||||
|
9.84633 * scaleX, |
||||
|
15.2359 * scaleY, |
||||
|
9.3916 * scaleX, |
||||
|
16.1904 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
8.252 * scaleX, |
||||
|
18.5828 * scaleY, |
||||
|
7.18153 * scaleX, |
||||
|
22.466 * scaleY, |
||||
|
6.2207 * scaleX, |
||||
|
27.5654 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
5.26481 * scaleX, |
||||
|
32.6387 * scaleY, |
||||
|
4.43215 * scaleX, |
||||
|
38.8273 * scaleY, |
||||
|
3.73535 * scaleX, |
||||
|
45.7744 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
2.34189 * scaleX, |
||||
|
59.6675 * scaleY, |
||||
|
1.49647 * scaleX, |
||||
|
76.5363 * scaleY, |
||||
|
1.27832 * scaleX, |
||||
|
93.4678 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
1.06017 * scaleX, |
||||
|
110.4 * scaleY, |
||||
|
1.47057 * scaleX, |
||||
|
127.372 * scaleY, |
||||
|
2.58301 * scaleX, |
||||
|
141.473 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
3.13928 * scaleX, |
||||
|
148.524 * scaleY, |
||||
|
3.86921 * scaleX, |
||||
|
154.841 * scaleY, |
||||
|
4.78125 * scaleX, |
||||
|
160.068 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
5.69748 * scaleX, |
||||
|
165.32 * scaleY, |
||||
|
6.78334 * scaleX, |
||||
|
169.385 * scaleY, |
||||
|
8.01367 * scaleX, |
||||
|
171.984 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
8.53417 * scaleX, |
||||
|
173.084 * scaleY, |
||||
|
9.59654 * scaleX, |
||||
|
174.216 * scaleY, |
||||
|
11.2891 * scaleX, |
||||
|
175.343 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
12.9722 * scaleX, |
||||
|
176.463 * scaleY, |
||||
|
15.1988 * scaleX, |
||||
|
177.524 * scaleY, |
||||
|
17.9219 * scaleX, |
||||
|
178.515 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
23.3679 * scaleX, |
||||
|
180.496 * scaleY, |
||||
|
30.6491 * scaleX, |
||||
|
182.138 * scaleY, |
||||
|
39.1807 * scaleX, |
||||
|
183.437 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
56.2336 * scaleX, |
||||
|
186.032 * scaleY, |
||||
|
78.0934 * scaleX, |
||||
|
187.222 * scaleY, |
||||
|
99.8242 * scaleX, |
||||
|
187.064 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
121.556 * scaleX, |
||||
|
186.906 * scaleY, |
||||
|
143.101 * scaleX, |
||||
|
185.4 * scaleY, |
||||
|
159.525 * scaleX, |
||||
|
182.622 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
167.745 * scaleX, |
||||
|
181.232 * scaleY, |
||||
|
174.627 * scaleX, |
||||
|
179.531 * scaleY, |
||||
|
179.594 * scaleX, |
||||
|
177.548 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
182.079 * scaleX, |
||||
|
176.556 * scaleY, |
||||
|
184.034 * scaleX, |
||||
|
175.512 * scaleY, |
||||
|
185.429 * scaleX, |
||||
|
174.437 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
186.83 * scaleX, |
||||
|
173.355 * scaleY, |
||||
|
187.568 * scaleX, |
||||
|
172.319 * scaleY, |
||||
|
187.812 * scaleX, |
||||
|
171.361 * scaleY, |
||||
|
) |
||||
|
..lineTo( |
||||
|
187.812 * scaleX, |
||||
|
171.361 * scaleY, |
||||
|
) // In SVG, this was H (horizontal line), equivalent to lineTo in Flutter |
||||
|
..cubicTo( |
||||
|
189.156 * scaleX, |
||||
|
166.074 * scaleY, |
||||
|
190.148 * scaleX, |
||||
|
155.525 * scaleY, |
||||
|
190.773 * scaleX, |
||||
|
142.157 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
191.396 * scaleX, |
||||
|
128.832 * scaleY, |
||||
|
191.651 * scaleX, |
||||
|
112.822 * scaleY, |
||||
|
191.552 * scaleX, |
||||
|
96.6875 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
191.453 * scaleX, |
||||
|
80.5539 * scaleY, |
||||
|
191.001 * scaleX, |
||||
|
64.3091 * scaleY, |
||||
|
190.213 * scaleX, |
||||
|
50.5156 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
189.423 * scaleX, |
||||
|
36.6928 * scaleY, |
||||
|
188.299 * scaleX, |
||||
|
25.4153 * scaleY, |
||||
|
186.876 * scaleX, |
||||
|
19.167 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
186.404 * scaleX, |
||||
|
17.0929 * scaleY, |
||||
|
185.566 * scaleX, |
||||
|
15.3424 * scaleY, |
||||
|
184.087 * scaleX, |
||||
|
14.1582 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
181.343 * scaleX, |
||||
|
11.9613 * scaleY, |
||||
|
176.72 * scaleX, |
||||
|
9.98089 * scaleY, |
||||
|
170.561 * scaleX, |
||||
|
8.27539 * scaleY, |
||||
|
) |
||||
|
..cubicTo( |
||||
|
164.434 * scaleX, |
||||
|
6.579 * scaleY, |
||||
|
156.914 * scaleX, |
||||
|
5.18731 * scaleY, |
||||
|
148.483 * scaleX, |
||||
|
4.10254 * scaleY, |
||||
|
) |
||||
|
..close(); // Closes the path to form a complete shape. |
||||
|
|
||||
|
return path; |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
bool shouldReclip(covariant CustomClipper<Path> oldClipper) { |
||||
|
return this != oldClipper; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,76 @@ |
|||||
|
import 'package:flutter/material.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/services/audio_service.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/my_device.dart'; |
||||
|
import 'package:hadi_hoda_flutter/init_bindings.dart'; |
||||
|
import 'package:pod_player/pod_player.dart'; |
||||
|
|
||||
|
class MyVideoPlayer extends StatefulWidget { |
||||
|
const MyVideoPlayer({super.key, required this.videoURL}); |
||||
|
|
||||
|
final String? videoURL; |
||||
|
|
||||
|
@override |
||||
|
State<MyVideoPlayer> createState() => _MyVideoPlayerState(); |
||||
|
} |
||||
|
|
||||
|
class _MyVideoPlayerState extends State<MyVideoPlayer> { |
||||
|
late final PodPlayerController _controller; |
||||
|
final AudioService _mainAudioService = locator( |
||||
|
instanceName: MyConstants.mainAudioService, |
||||
|
); |
||||
|
final AudioService _effectAudioService = locator( |
||||
|
instanceName: MyConstants.effectAudioService, |
||||
|
); |
||||
|
|
||||
|
@override |
||||
|
void initState() { |
||||
|
super.initState(); |
||||
|
_mainAudioService.stop(); |
||||
|
_effectAudioService.stop(); |
||||
|
_controller = PodPlayerController( |
||||
|
podPlayerConfig: const PodPlayerConfig( |
||||
|
autoPlay: false, |
||||
|
isLooping: false, |
||||
|
wakelockEnabled: true, |
||||
|
), |
||||
|
playVideoFrom: PlayVideoFrom.network(widget.videoURL ?? ''), |
||||
|
)..initialise(); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
void dispose() { |
||||
|
_controller.dispose(); |
||||
|
_mainAudioService.play(); |
||||
|
_effectAudioService.play(); |
||||
|
MyDevice.setPortrait(); |
||||
|
super.dispose(); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return Scaffold( |
||||
|
backgroundColor: MyColors.black, |
||||
|
extendBodyBehindAppBar: true, |
||||
|
appBar: AppBar( |
||||
|
backgroundColor: MyColors.transparent, |
||||
|
foregroundColor: MyColors.white, |
||||
|
), |
||||
|
body: PodVideoPlayer( |
||||
|
controller: _controller, |
||||
|
matchVideoAspectRatioToFrame: true, |
||||
|
matchFrameAspectRatioToVideo: true, |
||||
|
videoAspectRatio: _controller.videoPlayerValue?.aspectRatio ?? 16 / 9, |
||||
|
podProgressBarConfig: const PodProgressBarConfig(), |
||||
|
onToggleFullScreen: (isFullScreen) async { |
||||
|
if (isFullScreen) { |
||||
|
await MyDevice.setAllOrientations(); |
||||
|
} else { |
||||
|
await MyDevice.setPortrait(); |
||||
|
} |
||||
|
}, |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
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/utils/local_storage.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 IGuiderDatasource { |
||||
|
Future<LevelEntity> getLevel(); |
||||
|
} |
||||
|
|
||||
|
/// Local |
||||
|
class GuiderDatasourceImpl implements IGuiderDatasource { |
||||
|
const GuiderDatasourceImpl(); |
||||
|
|
||||
|
@override |
||||
|
Future<LevelEntity> getLevel() async { |
||||
|
try { |
||||
|
final String selectedLanguage = |
||||
|
LocalStorage.readData(key: MyConstants.selectLanguage) ?? |
||||
|
MyConstants.defaultLanguage; |
||||
|
final Box<TotalDataEntity> levelBox = Hive.box(MyConstants.levelBox); |
||||
|
final TotalDataEntity findData = levelBox.values.singleWhere( |
||||
|
(e) => e.code == selectedLanguage, |
||||
|
orElse: () => TotalDataEntity(), |
||||
|
); |
||||
|
final LevelEntity? findLevel = findData.nodes?.first.level; |
||||
|
return findLevel ?? LevelEntity(); |
||||
|
} catch (e) { |
||||
|
throw MyException(errorMessage: '$e'); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
import 'package:flutter/foundation.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/guider/data/datasource/guider_datasource.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/guider/domain/repository/guider_repository.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; |
||||
|
|
||||
|
class GuiderRepositoryImpl implements IGuiderRepository { |
||||
|
final IGuiderDatasource datasource; |
||||
|
|
||||
|
const GuiderRepositoryImpl(this.datasource); |
||||
|
|
||||
|
@override |
||||
|
Future<DataState<LevelEntity, MyException>> getLevel() async { |
||||
|
try { |
||||
|
final LevelEntity response = await datasource.getLevel(); |
||||
|
return DataState.success(response); |
||||
|
} on MyException catch (e) { |
||||
|
return DataState.error(e); |
||||
|
} catch (e) { |
||||
|
if (kDebugMode) { |
||||
|
rethrow; |
||||
|
} else { |
||||
|
return DataState.error(MyException(errorMessage: '$e')); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; |
||||
|
|
||||
|
abstract class IGuiderRepository { |
||||
|
Future<DataState<LevelEntity, MyException>> getLevel(); |
||||
|
} |
||||
@ -0,0 +1,17 @@ |
|||||
|
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/usecase/usecase.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/guider/domain/repository/guider_repository.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; |
||||
|
|
||||
|
class GetFirstLevelUseCase implements UseCase<LevelEntity, NoParams> { |
||||
|
final IGuiderRepository repository; |
||||
|
|
||||
|
const GetFirstLevelUseCase(this.repository); |
||||
|
|
||||
|
@override |
||||
|
Future<DataState<LevelEntity, MyException>> call(NoParams params) { |
||||
|
return repository.getLevel(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,131 @@ |
|||||
|
import 'dart:async'; |
||||
|
|
||||
|
import 'package:bloc/bloc.dart'; |
||||
|
import 'package:flutter/material.dart'; |
||||
|
import 'package:go_router/go_router.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/params/no_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/local_storage.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/my_context.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/guider/domain/usecases/get_first_level_usecase.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/guider/presentation/bloc/guider_event.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/guider/presentation/bloc/guider_state.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/question/domain/entity/question_entity.dart'; |
||||
|
import 'package:showcaseview/showcaseview.dart'; |
||||
|
|
||||
|
class GuiderBloc extends Bloc<GuiderEvent, GuiderState> { |
||||
|
/// ------------constructor------------ |
||||
|
GuiderBloc(this._getFirstLevelUseCase) : super(const GuiderState()) { |
||||
|
registerShowCase(); |
||||
|
on<GetFirstLevelEvent>(_getFirstLevelEvent); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
Future<void> close() { |
||||
|
unRegisterShowCase(); |
||||
|
animationController.dispose(); |
||||
|
return super.close(); |
||||
|
} |
||||
|
|
||||
|
/// ------------UseCases------------ |
||||
|
final GetFirstLevelUseCase _getFirstLevelUseCase; |
||||
|
|
||||
|
/// ------------Variables------------ |
||||
|
final Map<String, GlobalKey> showCaseKey = { |
||||
|
'answer_key_0': GlobalKey(), |
||||
|
'answer_key_1': GlobalKey(), |
||||
|
'answer_key_2': GlobalKey(), |
||||
|
'answer_key_3': GlobalKey(), |
||||
|
'notif_key_0': GlobalKey(), |
||||
|
'notif_key_1': GlobalKey(), |
||||
|
'notif_key_2': GlobalKey(), |
||||
|
'notif_key_3': GlobalKey(), |
||||
|
'stepper_key': GlobalKey(), |
||||
|
'hadith_key': GlobalKey(), |
||||
|
'guide_key': GlobalKey(), |
||||
|
}; |
||||
|
String? id; |
||||
|
|
||||
|
/// ------------Controllers------------ |
||||
|
late final AnimationController animationController; |
||||
|
|
||||
|
/// ------------Functions------------ |
||||
|
void registerShowCase() { |
||||
|
try { |
||||
|
ShowcaseView.register( |
||||
|
onStart: (showcaseIndex, key) { |
||||
|
LocalStorage.saveData(key: MyConstants.firstShowcase, value: 'true'); |
||||
|
}, |
||||
|
onDismiss: (onDismiss) { |
||||
|
MyContext.get.goNamed( |
||||
|
Routes.questionPage, |
||||
|
pathParameters: {'id': id ?? ''}, |
||||
|
); |
||||
|
}, |
||||
|
onFinish: () { |
||||
|
MyContext.get.goNamed( |
||||
|
Routes.questionPage, |
||||
|
pathParameters: {'id': id ?? ''}, |
||||
|
); |
||||
|
}, |
||||
|
); |
||||
|
} catch (_) {} |
||||
|
} |
||||
|
|
||||
|
void unRegisterShowCase() { |
||||
|
try { |
||||
|
ShowcaseView.get().unregister(); |
||||
|
} catch (_) {} |
||||
|
} |
||||
|
|
||||
|
void startShowcase() { |
||||
|
if (LocalStorage.readData(key: MyConstants.firstShowcase) != 'true') { |
||||
|
try { |
||||
|
ShowcaseView.get().startShowCase([ |
||||
|
showCaseKey['answer_key_1']!, |
||||
|
showCaseKey['notif_key_0']!, |
||||
|
showCaseKey['stepper_key']!, |
||||
|
showCaseKey['hadith_key']!, |
||||
|
showCaseKey['guide_key']!, |
||||
|
]); |
||||
|
} catch (_) {} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/// ------------Event Calls------------ |
||||
|
|
||||
|
FutureOr<void> _getFirstLevelEvent( |
||||
|
GetFirstLevelEvent event, |
||||
|
Emitter<GuiderState> emit, |
||||
|
) async { |
||||
|
id = event.id; |
||||
|
await _getFirstLevelUseCase(NoParams()).then( |
||||
|
(value) => value.fold((data) async { |
||||
|
final LevelEntity level = LevelEntity( |
||||
|
id: data.id, |
||||
|
order: data.order, |
||||
|
title: data.title, |
||||
|
questions: [ |
||||
|
...?data.questions, |
||||
|
QuestionEntity(order: (data.questions?.length ?? 0) + 1), |
||||
|
], |
||||
|
); |
||||
|
emit( |
||||
|
state.copyWith( |
||||
|
getQuestionStatus: const BaseComplete(''), |
||||
|
levelEntity: level, |
||||
|
currentQuestion: data.questions?.first, |
||||
|
), |
||||
|
); |
||||
|
await Future.delayed(const Duration(milliseconds: 300), () { |
||||
|
animationController.forward().then((value) { |
||||
|
startShowcase(); |
||||
|
}); |
||||
|
}); |
||||
|
}, (error) {}), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
sealed class GuiderEvent { |
||||
|
const GuiderEvent(); |
||||
|
} |
||||
|
|
||||
|
class GetFirstLevelEvent extends GuiderEvent { |
||||
|
final String? id; |
||||
|
const GetFirstLevelEvent({this.id}); |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
import 'package:hadi_hoda_flutter/core/status/base_status.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/question/domain/entity/question_entity.dart'; |
||||
|
|
||||
|
class GuiderState { |
||||
|
final BaseStatus getQuestionStatus; |
||||
|
final LevelEntity? levelEntity; |
||||
|
final QuestionEntity? currentQuestion; |
||||
|
|
||||
|
const GuiderState({ |
||||
|
this.getQuestionStatus = const BaseInit(), |
||||
|
this.levelEntity, |
||||
|
this.currentQuestion, |
||||
|
}); |
||||
|
|
||||
|
GuiderState copyWith({ |
||||
|
BaseStatus? getQuestionStatus, |
||||
|
LevelEntity? levelEntity, |
||||
|
QuestionEntity? currentQuestion, |
||||
|
}) { |
||||
|
return GuiderState( |
||||
|
getQuestionStatus: getQuestionStatus ?? this.getQuestionStatus, |
||||
|
levelEntity: levelEntity ?? this.levelEntity, |
||||
|
currentQuestion: currentQuestion ?? this.currentQuestion, |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,248 @@ |
|||||
|
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_audios.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_text_style.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/utils/gap.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/set_platform_size.dart'; |
||||
|
import 'package:hadi_hoda_flutter/core/widgets/animations/fade_anim.dart'; |
||||
|
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_up_fade.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/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_state.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.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_stepper.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/question_title.dart'; |
||||
|
|
||||
|
class GuiderPage extends StatefulWidget { |
||||
|
const GuiderPage({super.key}); |
||||
|
|
||||
|
@override |
||||
|
State<GuiderPage> createState() => _GuiderPageState(); |
||||
|
} |
||||
|
|
||||
|
class _GuiderPageState extends State<GuiderPage> with TickerProviderStateMixin { |
||||
|
|
||||
|
@override |
||||
|
void initState() { |
||||
|
super.initState(); |
||||
|
context |
||||
|
.read<GuiderBloc>() |
||||
|
.animationController = AnimationController( |
||||
|
vsync: this, |
||||
|
duration: const Duration(milliseconds: 500), |
||||
|
reverseDuration: const Duration(milliseconds: 500), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
Widget build(BuildContext context) { |
||||
|
return Scaffold( |
||||
|
body: MyPopScope( |
||||
|
backHome: true, |
||||
|
child: Directionality( |
||||
|
textDirection: TextDirection.ltr, |
||||
|
child: Container( |
||||
|
height: context.heightScreen, |
||||
|
width: context.widthScreen, |
||||
|
padding: EdgeInsets.symmetric( |
||||
|
horizontal: |
||||
|
setSize( |
||||
|
context: context, |
||||
|
mobile: MySpaces.s16, |
||||
|
tablet: MySpaces.s30, |
||||
|
) ?? |
||||
|
0, |
||||
|
), |
||||
|
decoration: BoxDecoration( |
||||
|
gradient: const LinearGradient( |
||||
|
begin: Alignment.topCenter, |
||||
|
end: Alignment.bottomCenter, |
||||
|
colors: [Color(0XFF6930DA), Color(0XFF263AA1)], |
||||
|
), |
||||
|
image: DecorationImage( |
||||
|
image: const AssetImage(MyAssets.pattern), |
||||
|
scale: 3, |
||||
|
repeat: ImageRepeat.repeat, |
||||
|
colorFilter: ColorFilter.mode( |
||||
|
Colors.black.withValues(alpha: 0.3), |
||||
|
BlendMode.srcIn, |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
child: Column( |
||||
|
children: [ |
||||
|
setPlatform<double>( |
||||
|
android: MySpaces.s20, |
||||
|
iOS: 50, |
||||
|
)?.gapHeight ?? |
||||
|
const SizedBox.shrink(), |
||||
|
_topButtons(context), |
||||
|
MySpaces.s10.gapHeight, |
||||
|
Expanded( |
||||
|
child: Column( |
||||
|
children: [ |
||||
|
_stepper(context), |
||||
|
_titles(context), |
||||
|
MySpaces.s20.gapHeight, |
||||
|
Expanded(child: _answers(context)), |
||||
|
_bottom(context), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
setPlatform<double>(android: MySpaces.s20)?.gapHeight ?? |
||||
|
const SizedBox.shrink(), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
Widget _topButtons(BuildContext context) { |
||||
|
return SlideDownFade( |
||||
|
child: Row( |
||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
||||
|
crossAxisAlignment: CrossAxisAlignment.center, |
||||
|
children: [ |
||||
|
GlassyButton( |
||||
|
image: MyAssets.home, |
||||
|
audio: MyAudios.back, |
||||
|
onTap: () {}, |
||||
|
), |
||||
|
BlocBuilder<GuiderBloc, GuiderState>( |
||||
|
builder: (context, state) => |
||||
|
QuestionTitle( |
||||
|
step: state.levelEntity?.order, |
||||
|
currentQuestion: state.currentQuestion?.order, |
||||
|
questionLength: state.levelEntity?.questions?.length, |
||||
|
), |
||||
|
), |
||||
|
GlassyButton(image: MyAssets.music, onTap: () {}), |
||||
|
], |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
Widget _stepper(BuildContext context) { |
||||
|
return FadeAnim( |
||||
|
child: MyShowcaseWidget( |
||||
|
globalKey: context |
||||
|
.read<GuiderBloc>() |
||||
|
.showCaseKey['stepper_key']!, |
||||
|
description: context.translate.showcase_stepper, |
||||
|
child: const QuestionStepper(length: 4, currentStep: 1), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
Widget _titles(BuildContext context) { |
||||
|
return BlocBuilder<GuiderBloc, GuiderState>( |
||||
|
builder: (context, state) => |
||||
|
FadeAnim( |
||||
|
child: Text( |
||||
|
state.currentQuestion?.title ?? '', |
||||
|
textAlign: TextAlign.center, |
||||
|
maxLines: 3, |
||||
|
style: MYTextStyle.titr1.copyWith( |
||||
|
shadows: [ |
||||
|
BoxShadow( |
||||
|
offset: const Offset(0, 2), |
||||
|
color: MyColors.black.withValues(alpha: 0.25), |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
Widget _answers(BuildContext context) { |
||||
|
return BlocBuilder<GuiderBloc, GuiderState>( |
||||
|
builder: (context, state) => |
||||
|
GridView.builder( |
||||
|
itemCount: state.currentQuestion?.answers?.length ?? 0, |
||||
|
physics: const NeverScrollableScrollPhysics(), |
||||
|
shrinkWrap: true, |
||||
|
clipBehavior: Clip.none, |
||||
|
padding: EdgeInsets.symmetric( |
||||
|
horizontal: setSize(context: context, tablet: 70) ?? 0, |
||||
|
), |
||||
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( |
||||
|
crossAxisCount: 2, |
||||
|
crossAxisSpacing: MySpaces.s20, |
||||
|
mainAxisSpacing: 20, |
||||
|
childAspectRatio: 0.75, |
||||
|
), |
||||
|
itemBuilder: (context, index) => |
||||
|
state.currentQuestion?.answers?[index].imageId == null |
||||
|
? const SizedBox.shrink() |
||||
|
: SlideAnim( |
||||
|
controller: context.read<GuiderBloc>().animationController, |
||||
|
index: index, |
||||
|
child: AnswerBoxShowCase( |
||||
|
globalKey: context |
||||
|
.read<GuiderBloc>() |
||||
|
.showCaseKey['notif_key_$index']!, |
||||
|
answerGlobalKey: context |
||||
|
.read<GuiderBloc>() |
||||
|
.showCaseKey['answer_key_$index']!, |
||||
|
index: state.currentQuestion?.answers?[index].order ?? 1, |
||||
|
answer: |
||||
|
state.currentQuestion?.answers?[index] ?? AnswerEntity(), |
||||
|
correctAnswer: 0, |
||||
|
onNotifTap: (AnswerEntity answer) {}, |
||||
|
onTap: (isCorrect, correctAnswer) {}, |
||||
|
), |
||||
|
), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
Widget _bottom(BuildContext context) { |
||||
|
return SlideUpFade( |
||||
|
child: SizedBox( |
||||
|
width: context.widthScreen, |
||||
|
child: Stack( |
||||
|
alignment: AlignmentDirectional.centerStart, |
||||
|
children: [ |
||||
|
MyShowcaseWidget( |
||||
|
globalKey: context |
||||
|
.read<GuiderBloc>() |
||||
|
.showCaseKey['guide_key']!, |
||||
|
description: context.translate.showcase_guide, |
||||
|
type: ShowcaseTooltipType.top, |
||||
|
child: MyImage( |
||||
|
image: MyAssets.globe, |
||||
|
fit: BoxFit.cover, |
||||
|
size: setSize(context: context, tablet: 120), |
||||
|
), |
||||
|
), |
||||
|
PositionedDirectional( |
||||
|
end: 0, |
||||
|
child: MyShowcaseWidget( |
||||
|
globalKey: context |
||||
|
.read<GuiderBloc>() |
||||
|
.showCaseKey['hadith_key']!, |
||||
|
type: ShowcaseTooltipType.topLeft, |
||||
|
description: context.translate.showcase_hadith, |
||||
|
child: GlassyButton(image: MyAssets.leaf, onTap: () {}), |
||||
|
), |
||||
|
), |
||||
|
], |
||||
|
), |
||||
|
), |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -1,54 +1,24 @@ |
|||||
import 'package:flutter/material.dart'; |
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/core/utils/my_localization.dart'; |
|
||||
|
import 'package:flutter_bloc/flutter_bloc.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/widgets/animations/slide_up_fade.dart'; |
|
||||
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; |
|
||||
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/widgets/bubble_chat_widget.dart'; |
|
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_bloc.dart'; |
||||
|
import 'package:pod_player/pod_player.dart'; |
||||
|
|
||||
class Intro1Screen extends StatelessWidget { |
class Intro1Screen extends StatelessWidget { |
||||
const Intro1Screen({super.key}); |
const Intro1Screen({super.key}); |
||||
|
|
||||
@override |
@override |
||||
Widget build(BuildContext context) { |
Widget build(BuildContext context) { |
||||
return Stack( |
|
||||
alignment: Alignment.center, |
|
||||
children: [ |
|
||||
Positioned.fill( |
|
||||
child: MyImage(image: MyAssets.intro_1, fit: BoxFit.cover), |
|
||||
|
return SizedBox.expand( |
||||
|
child: PodVideoPlayer( |
||||
|
controller: context.read<IntroBloc>().podController1, |
||||
|
overlayBuilder: (options) => const SizedBox.shrink(), |
||||
|
alwaysShowProgressBar: false, |
||||
|
videoAspectRatio: context.widthScreen / context.heightScreen, |
||||
|
frameAspectRatio: context.widthScreen / context.heightScreen, |
||||
|
backgroundColor: const Color(0XFF00154C), |
||||
|
onLoading: (context) => const SizedBox.shrink(), |
||||
), |
), |
||||
PositionedDirectional( |
|
||||
end: setSize<double>( |
|
||||
context: context, |
|
||||
mobile: MySpaces.s10, |
|
||||
tablet: 0.15.w, |
|
||||
), |
|
||||
top: setSize<double>( |
|
||||
context: context, |
|
||||
mobile: 0.15.h, |
|
||||
tablet: 0.25.h, |
|
||||
), |
|
||||
width: 290, |
|
||||
child: SlideUpFade( |
|
||||
delay: Duration(milliseconds: 300), |
|
||||
child: BubbleChatWidget( |
|
||||
text: context.translate.intro_1_1, |
|
||||
flip: true, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
PositionedDirectional( |
|
||||
start: setSize(context: context, mobile: MySpaces.s30, tablet: 0.2.w), |
|
||||
top: setSize(context: context, mobile: 0.3.h, tablet: 0.35.h), |
|
||||
width: 250, |
|
||||
child: SlideUpFade( |
|
||||
delay: Duration(milliseconds: 800), |
|
||||
child: BubbleChatWidget(text: context.translate.intro_1_2), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
); |
); |
||||
} |
} |
||||
} |
} |
||||
@ -1,37 +1,25 @@ |
|||||
import 'package:flutter/material.dart'; |
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/core/utils/my_localization.dart'; |
|
||||
|
import 'package:flutter_bloc/flutter_bloc.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.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/widgets/animations/slide_up_fade.dart'; |
|
||||
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; |
|
||||
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/widgets/bubble_chat_widget.dart'; |
|
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_bloc.dart'; |
||||
|
import 'package:pod_player/pod_player.dart'; |
||||
|
|
||||
class Intro2Screen extends StatelessWidget { |
class Intro2Screen extends StatelessWidget { |
||||
const Intro2Screen({super.key}); |
const Intro2Screen({super.key}); |
||||
|
|
||||
@override |
@override |
||||
Widget build(BuildContext context) { |
Widget build(BuildContext context) { |
||||
return Stack( |
|
||||
alignment: Alignment.center, |
|
||||
children: [ |
|
||||
Positioned.fill( |
|
||||
child: Transform.flip( |
|
||||
flipX: true, |
|
||||
child: MyImage(image: MyAssets.intro_2, fit: BoxFit.cover), |
|
||||
|
return SizedBox.expand( |
||||
|
child: PodVideoPlayer( |
||||
|
controller: context.read<IntroBloc>().podController2, |
||||
|
overlayBuilder: (options) => const SizedBox.shrink(), |
||||
|
alwaysShowProgressBar: false, |
||||
|
videoAspectRatio: context.widthScreen / context.heightScreen, |
||||
|
frameAspectRatio: context.widthScreen / context.heightScreen, |
||||
|
backgroundColor: MyColors.transparent, |
||||
|
onLoading: (context) => const SizedBox.shrink(), |
||||
), |
), |
||||
), |
|
||||
PositionedDirectional( |
|
||||
end: setSize(context: context, mobile: MySpaces.s40, tablet: 0.3.w), |
|
||||
top: setSize(context: context, mobile: 0.17.h, tablet: 0.23.h), |
|
||||
width: 250, |
|
||||
child: SlideUpFade( |
|
||||
delay: Duration(milliseconds: 300), |
|
||||
child: BubbleChatWidget(text: context.translate.intro_2), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
); |
); |
||||
} |
} |
||||
} |
} |
||||
@ -1,37 +1,25 @@ |
|||||
import 'package:flutter/material.dart'; |
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/core/utils/my_localization.dart'; |
|
||||
|
import 'package:flutter_bloc/flutter_bloc.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.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/widgets/animations/slide_up_fade.dart'; |
|
||||
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; |
|
||||
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/widgets/bubble_chat_widget.dart'; |
|
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_bloc.dart'; |
||||
|
import 'package:pod_player/pod_player.dart'; |
||||
|
|
||||
class Intro3Screen extends StatelessWidget { |
class Intro3Screen extends StatelessWidget { |
||||
const Intro3Screen({super.key}); |
const Intro3Screen({super.key}); |
||||
|
|
||||
@override |
@override |
||||
Widget build(BuildContext context) { |
Widget build(BuildContext context) { |
||||
return Stack( |
|
||||
alignment: Alignment.center, |
|
||||
children: [ |
|
||||
Positioned.fill( |
|
||||
child: MyImage(image: MyAssets.intro_3, fit: BoxFit.cover), |
|
||||
|
return SizedBox.expand( |
||||
|
child: PodVideoPlayer( |
||||
|
controller: context.read<IntroBloc>().podController3, |
||||
|
overlayBuilder: (options) => const SizedBox.shrink(), |
||||
|
alwaysShowProgressBar: false, |
||||
|
videoAspectRatio: context.widthScreen / context.heightScreen, |
||||
|
frameAspectRatio: context.widthScreen / context.heightScreen, |
||||
|
backgroundColor: MyColors.transparent, |
||||
|
onLoading: (context) => const SizedBox.shrink(), |
||||
), |
), |
||||
PositionedDirectional( |
|
||||
start: setSize(context: context, mobile: MySpaces.s30), |
|
||||
top: setSize(context: context, mobile: 180, tablet: 0.25.h), |
|
||||
width: 270, |
|
||||
child: SlideUpFade( |
|
||||
delay: Duration(milliseconds: 300), |
|
||||
child: BubbleChatWidget( |
|
||||
text: context.translate.intro_3, |
|
||||
flip: true, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
); |
); |
||||
} |
} |
||||
} |
} |
||||
@ -1,36 +1,25 @@ |
|||||
import 'package:flutter/material.dart'; |
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/core/utils/my_localization.dart'; |
|
||||
|
import 'package:flutter_bloc/flutter_bloc.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.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/widgets/animations/slide_up_fade.dart'; |
|
||||
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; |
|
||||
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/widgets/bubble_chat_widget.dart'; |
|
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_bloc.dart'; |
||||
|
import 'package:pod_player/pod_player.dart'; |
||||
|
|
||||
class Intro4Screen extends StatelessWidget { |
class Intro4Screen extends StatelessWidget { |
||||
const Intro4Screen({super.key}); |
const Intro4Screen({super.key}); |
||||
|
|
||||
@override |
@override |
||||
Widget build(BuildContext context) { |
Widget build(BuildContext context) { |
||||
return Stack( |
|
||||
alignment: Alignment.center, |
|
||||
children: [ |
|
||||
Positioned.fill( |
|
||||
child: MyImage(image: MyAssets.intro_4, fit: BoxFit.cover), |
|
||||
|
return SizedBox.expand( |
||||
|
child: PodVideoPlayer( |
||||
|
controller: context.read<IntroBloc>().podController4, |
||||
|
overlayBuilder: (options) => const SizedBox.shrink(), |
||||
|
alwaysShowProgressBar: false, |
||||
|
videoAspectRatio: context.widthScreen / context.heightScreen, |
||||
|
frameAspectRatio: context.widthScreen / context.heightScreen, |
||||
|
backgroundColor: MyColors.transparent, |
||||
|
onLoading: (context) => const SizedBox.shrink(), |
||||
), |
), |
||||
PositionedDirectional( |
|
||||
start: setSize(context: context, mobile: MySpaces.s10, tablet: 0.2.w), |
|
||||
bottom: setSize(context: context, mobile: 0.4.h, tablet: 0.4.h), |
|
||||
width: 237, |
|
||||
child: SlideUpFade( |
|
||||
delay: Duration(milliseconds: 300), |
|
||||
child: BubbleChatWidget( |
|
||||
text: context.translate.intro_4, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
); |
); |
||||
} |
} |
||||
} |
} |
||||
@ -1,34 +1,25 @@ |
|||||
import 'package:flutter/material.dart'; |
import 'package:flutter/material.dart'; |
||||
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
|
||||
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; |
|
||||
|
import 'package:flutter_bloc/flutter_bloc.dart'; |
||||
|
import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.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/widgets/animations/slide_up_fade.dart'; |
|
||||
import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; |
|
||||
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/widgets/bubble_chat_widget.dart'; |
|
||||
|
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_bloc.dart'; |
||||
|
import 'package:pod_player/pod_player.dart'; |
||||
|
|
||||
class Intro5Screen extends StatelessWidget { |
class Intro5Screen extends StatelessWidget { |
||||
const Intro5Screen({super.key}); |
const Intro5Screen({super.key}); |
||||
|
|
||||
@override |
@override |
||||
Widget build(BuildContext context) { |
Widget build(BuildContext context) { |
||||
return Stack( |
|
||||
alignment: Alignment.center, |
|
||||
children: [ |
|
||||
Positioned.fill( |
|
||||
child: MyImage(image: MyAssets.intro_5, fit: BoxFit.cover), |
|
||||
|
return SizedBox.expand( |
||||
|
child: PodVideoPlayer( |
||||
|
controller: context.read<IntroBloc>().podController5, |
||||
|
overlayBuilder: (options) => const SizedBox.shrink(), |
||||
|
alwaysShowProgressBar: false, |
||||
|
videoAspectRatio: context.widthScreen / context.heightScreen, |
||||
|
frameAspectRatio: context.widthScreen / context.heightScreen, |
||||
|
backgroundColor: MyColors.transparent, |
||||
|
onLoading: (context) => const SizedBox.shrink(), |
||||
), |
), |
||||
PositionedDirectional( |
|
||||
top: setSize(context: context, mobile: 0.37.h, tablet: 0.42.h), |
|
||||
width: 200, |
|
||||
child: SlideUpFade( |
|
||||
delay: Duration(milliseconds: 300), |
|
||||
child: BubbleChatWidget( |
|
||||
text: context.translate.intro_5, |
|
||||
), |
|
||||
), |
|
||||
), |
|
||||
], |
|
||||
); |
); |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
import 'package:hadi_hoda_flutter/features/level/data/model/level_model.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/level/data/model/prize_model.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/level/domain/entity/node_entity.dart'; |
||||
|
|
||||
|
class NodeModel extends NodeEntity { |
||||
|
NodeModel({super.nodeType, super.level, super.prize}); |
||||
|
|
||||
|
factory NodeModel.fromJson(Map<String, dynamic> json) { |
||||
|
return NodeModel( |
||||
|
nodeType: json['node_type'] == null |
||||
|
? null |
||||
|
: NodeType.fromJson[json['node_type']], |
||||
|
level: json['node_type'] == null |
||||
|
? null |
||||
|
: json['node_type'] == 'level' |
||||
|
? LevelModel.fromJson(json['data']) |
||||
|
: null, |
||||
|
prize: json['node_type'] == null |
||||
|
? null |
||||
|
: json['node_type'] == 'prize' |
||||
|
? PrizeModel.fromJson(json['data']) |
||||
|
: null, |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
import 'package:hadi_hoda_flutter/features/level/domain/entity/prize_entity.dart'; |
||||
|
|
||||
|
class PrizeModel extends PrizeEntity { |
||||
|
PrizeModel({ |
||||
|
super.id, |
||||
|
super.afterLevel, |
||||
|
super.title, |
||||
|
super.imageURL, |
||||
|
super.animationURL, |
||||
|
}); |
||||
|
|
||||
|
factory PrizeModel.fromJson(Map<String, dynamic> json) { |
||||
|
return PrizeModel( |
||||
|
id: json['id'], |
||||
|
afterLevel: json['after_level'], |
||||
|
title: json['title'], |
||||
|
imageURL: json['image_url'], |
||||
|
animationURL: json['animation_url'], |
||||
|
); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; |
||||
|
import 'package:hadi_hoda_flutter/features/level/domain/entity/prize_entity.dart'; |
||||
|
import 'package:hive/hive.dart'; |
||||
|
|
||||
|
part 'node_entity.g.dart'; |
||||
|
|
||||
|
@HiveType(typeId: 8) |
||||
|
enum NodeType { |
||||
|
@HiveField(0) |
||||
|
level, |
||||
|
@HiveField(1) |
||||
|
prize; |
||||
|
|
||||
|
static Map<String, NodeType> get fromJson => { |
||||
|
'level': NodeType.level, |
||||
|
'prize': NodeType.prize, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
@HiveType(typeId: 7) |
||||
|
class NodeEntity extends HiveObject { |
||||
|
@HiveField(0) |
||||
|
NodeType? nodeType; |
||||
|
@HiveField(1) |
||||
|
LevelEntity? level; |
||||
|
@HiveField(2) |
||||
|
PrizeEntity? prize; |
||||
|
|
||||
|
NodeEntity({this.nodeType, this.level, this.prize}); |
||||
|
} |
||||
@ -0,0 +1,86 @@ |
|||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND |
||||
|
|
||||
|
part of 'node_entity.dart'; |
||||
|
|
||||
|
// ************************************************************************** |
||||
|
// TypeAdapterGenerator |
||||
|
// ************************************************************************** |
||||
|
|
||||
|
class NodeEntityAdapter extends TypeAdapter<NodeEntity> { |
||||
|
@override |
||||
|
final int typeId = 7; |
||||
|
|
||||
|
@override |
||||
|
NodeEntity read(BinaryReader reader) { |
||||
|
final numOfFields = reader.readByte(); |
||||
|
final fields = <int, dynamic>{ |
||||
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), |
||||
|
}; |
||||
|
return NodeEntity( |
||||
|
nodeType: fields[0] as NodeType?, |
||||
|
level: fields[1] as LevelEntity?, |
||||
|
prize: fields[2] as PrizeEntity?, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
void write(BinaryWriter writer, NodeEntity obj) { |
||||
|
writer |
||||
|
..writeByte(3) |
||||
|
..writeByte(0) |
||||
|
..write(obj.nodeType) |
||||
|
..writeByte(1) |
||||
|
..write(obj.level) |
||||
|
..writeByte(2) |
||||
|
..write(obj.prize); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
int get hashCode => typeId.hashCode; |
||||
|
|
||||
|
@override |
||||
|
bool operator ==(Object other) => |
||||
|
identical(this, other) || |
||||
|
other is NodeEntityAdapter && |
||||
|
runtimeType == other.runtimeType && |
||||
|
typeId == other.typeId; |
||||
|
} |
||||
|
|
||||
|
class NodeTypeAdapter extends TypeAdapter<NodeType> { |
||||
|
@override |
||||
|
final int typeId = 8; |
||||
|
|
||||
|
@override |
||||
|
NodeType read(BinaryReader reader) { |
||||
|
switch (reader.readByte()) { |
||||
|
case 0: |
||||
|
return NodeType.level; |
||||
|
case 1: |
||||
|
return NodeType.prize; |
||||
|
default: |
||||
|
return NodeType.level; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
void write(BinaryWriter writer, NodeType obj) { |
||||
|
switch (obj) { |
||||
|
case NodeType.level: |
||||
|
writer.writeByte(0); |
||||
|
break; |
||||
|
case NodeType.prize: |
||||
|
writer.writeByte(1); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
int get hashCode => typeId.hashCode; |
||||
|
|
||||
|
@override |
||||
|
bool operator ==(Object other) => |
||||
|
identical(this, other) || |
||||
|
other is NodeTypeAdapter && |
||||
|
runtimeType == other.runtimeType && |
||||
|
typeId == other.typeId; |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
import 'package:hive/hive.dart'; |
||||
|
|
||||
|
part 'prize_entity.g.dart'; |
||||
|
|
||||
|
@HiveType(typeId: 6) |
||||
|
class PrizeEntity extends HiveObject { |
||||
|
@HiveField(0) |
||||
|
int? id; |
||||
|
@HiveField(1) |
||||
|
int? afterLevel; |
||||
|
@HiveField(2) |
||||
|
String? title; |
||||
|
@HiveField(3) |
||||
|
String? imageURL; |
||||
|
@HiveField(4) |
||||
|
String? animationURL; |
||||
|
|
||||
|
PrizeEntity({ |
||||
|
this.id, |
||||
|
this.afterLevel, |
||||
|
this.title, |
||||
|
this.imageURL, |
||||
|
this.animationURL, |
||||
|
}); |
||||
|
} |
||||
@ -0,0 +1,53 @@ |
|||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND |
||||
|
|
||||
|
part of 'prize_entity.dart'; |
||||
|
|
||||
|
// ************************************************************************** |
||||
|
// TypeAdapterGenerator |
||||
|
// ************************************************************************** |
||||
|
|
||||
|
class PrizeEntityAdapter extends TypeAdapter<PrizeEntity> { |
||||
|
@override |
||||
|
final int typeId = 6; |
||||
|
|
||||
|
@override |
||||
|
PrizeEntity read(BinaryReader reader) { |
||||
|
final numOfFields = reader.readByte(); |
||||
|
final fields = <int, dynamic>{ |
||||
|
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), |
||||
|
}; |
||||
|
return PrizeEntity( |
||||
|
id: fields[0] as int?, |
||||
|
afterLevel: fields[1] as int?, |
||||
|
title: fields[2] as String?, |
||||
|
imageURL: fields[3] as String?, |
||||
|
animationURL: fields[4] as String?, |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
void write(BinaryWriter writer, PrizeEntity obj) { |
||||
|
writer |
||||
|
..writeByte(5) |
||||
|
..writeByte(0) |
||||
|
..write(obj.id) |
||||
|
..writeByte(1) |
||||
|
..write(obj.afterLevel) |
||||
|
..writeByte(2) |
||||
|
..write(obj.title) |
||||
|
..writeByte(3) |
||||
|
..write(obj.imageURL) |
||||
|
..writeByte(4) |
||||
|
..write(obj.animationURL); |
||||
|
} |
||||
|
|
||||
|
@override |
||||
|
int get hashCode => typeId.hashCode; |
||||
|
|
||||
|
@override |
||||
|
bool operator ==(Object other) => |
||||
|
identical(this, other) || |
||||
|
other is PrizeEntityAdapter && |
||||
|
runtimeType == other.runtimeType && |
||||
|
typeId == other.typeId; |
||||
|
} |
||||
@ -1,8 +1,10 @@ |
|||||
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/params/level_params.dart'; |
import 'package:hadi_hoda_flutter/core/params/level_params.dart'; |
||||
import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; |
import 'package:hadi_hoda_flutter/core/utils/data_state.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'; |
||||
|
|
||||
abstract class ILevelRepository { |
abstract class ILevelRepository { |
||||
Future<DataState<List<LevelEntity>, MyException>> getLevels({required LevelParams params}); |
|
||||
|
Future<DataState<List<NodeEntity>, MyException>> getLevels({ |
||||
|
required LevelParams params, |
||||
|
}); |
||||
} |
} |
||||
Some files were not shown because too many files changed in this diff
Write
Preview
Loading…
Cancel
Save
Reference in new issue