diff --git a/assets/images/gift.png b/assets/images/gift.png
new file mode 100644
index 0000000..1dfe4c4
Binary files /dev/null and b/assets/images/gift.png differ
diff --git a/assets/images/gift_background.png b/assets/images/gift_background.png
new file mode 100644
index 0000000..ad96c61
Binary files /dev/null and b/assets/images/gift_background.png differ
diff --git a/assets/images/gift_disable.png b/assets/images/gift_disable.png
new file mode 100644
index 0000000..d161107
Binary files /dev/null and b/assets/images/gift_disable.png differ
diff --git a/assets/svg/icon_play_video.svg b/assets/svg/icon_play_video.svg
new file mode 100644
index 0000000..5386a41
--- /dev/null
+++ b/assets/svg/icon_play_video.svg
@@ -0,0 +1,10 @@
+
diff --git a/lib/common_ui/resources/my_assets.dart b/lib/common_ui/resources/my_assets.dart
index 2153575..59ccf34 100644
--- a/lib/common_ui/resources/my_assets.dart
+++ b/lib/common_ui/resources/my_assets.dart
@@ -36,6 +36,9 @@ class MyAssets {
static const String satellite = 'assets/images/satellite.png';
static const String planetFinal = 'assets/images/planet_final.png';
static const String behindDiamond = 'assets/images/behind_diamond.png';
+ static const String gift = 'assets/images/gift.png';
+ static const String giftDisable = 'assets/images/gift_disable.png';
+ static const String giftBackground = 'assets/images/gift_background.png';
/// SVG
static const String closeBtn = 'assets/svg/close_btn.svg';
@@ -69,6 +72,7 @@ class MyAssets {
static const String diamondContainer = 'assets/svg/diamond_container.svg';
static const String iconPlay = 'assets/svg/icon_play.svg';
static const String iconNotif = 'assets/svg/icon_notif.svg';
+ static const String iconPlayVideo = 'assets/svg/icon_play_video.svg';
static final List images = [
backgroundHome,
@@ -103,5 +107,8 @@ class MyAssets {
satellite,
planetFinal,
behindDiamond,
+ gift,
+ giftDisable,
+ giftBackground,
];
}
\ No newline at end of file
diff --git a/lib/core/constants/my_api.dart b/lib/core/constants/my_api.dart
index 0e24eaa..a8aebd5 100644
--- a/lib/core/constants/my_api.dart
+++ b/lib/core/constants/my_api.dart
@@ -10,7 +10,7 @@ class MyApi {
static const String baseUrl = 'https://hadihoda.newhorizonco.uk/api';
- static const String levels = '/quiz/optimized/v2/levels/';
+ static const String levels = '/quiz/optimized/v3/levels/';
static const String images = '/quiz/optimized/download-all-files/images/';
static const String audios = '/quiz/optimized/v2/download-all-files/audio/';
}
diff --git a/lib/core/routers/my_routes.dart b/lib/core/routers/my_routes.dart
index bab2866..e149e3c 100644
--- a/lib/core/routers/my_routes.dart
+++ b/lib/core/routers/my_routes.dart
@@ -3,6 +3,7 @@ import 'package:go_router/go_router.dart';
import 'package:hadi_hoda_flutter/core/constants/my_constants.dart';
import 'package:hadi_hoda_flutter/core/middlewares/my_middlewares.dart';
import 'package:hadi_hoda_flutter/core/utils/my_context.dart';
+import 'package:hadi_hoda_flutter/core/widgets/video/my_video_player.dart';
import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_bloc.dart';
import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_event.dart';
import 'package:hadi_hoda_flutter/features/download/presentation/ui/download_page.dart';
@@ -42,6 +43,7 @@ class Routes {
static const String questionPage = '/question_page';
static const String guiderPage = '/guider_page';
static const String levelPage = '/level_page';
+ static const String videoPage = '/video_page';
}
final GoRouter appPages = _appPages();
@@ -147,5 +149,11 @@ GoRouter _appPages() => GoRouter(
child: const QuestionPage(),
),
),
+ GoRoute(
+ name: Routes.videoPage,
+ path: Routes.videoPage,
+ builder: (context, state) =>
+ MyVideoPlayer(videoURL: state.extra as String),
+ ),
],
);
\ No newline at end of file
diff --git a/lib/core/widgets/dialog/reward_dialog.dart b/lib/core/widgets/dialog/reward_dialog.dart
new file mode 100644
index 0000000..cb63843
--- /dev/null
+++ b/lib/core/widgets/dialog/reward_dialog.dart
@@ -0,0 +1,381 @@
+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 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: Color(0XFF322386),
+ fontSize: 22,
+ ),
+ ),
+ Text(
+ prize.title ?? '',
+ style: MYTextStyle.titr0.copyWith(
+ color: 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 ?? '',
+ fit: BoxFit.cover,
+ ),
+ 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 = 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 {
+ @override
+ Path getClip(Size size) {
+ // Original SVG dimensions to calculate the scaling factors.
+ final double originalWidth = 193.0;
+ final 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 oldClipper) {
+ return this != oldClipper;
+ }
+}
diff --git a/lib/core/widgets/video/my_video_player.dart b/lib/core/widgets/video/my_video_player.dart
new file mode 100644
index 0000000..7e37ffb
--- /dev/null
+++ b/lib/core/widgets/video/my_video_player.dart
@@ -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 createState() => _MyVideoPlayerState();
+}
+
+class _MyVideoPlayerState extends State {
+ 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: 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: PodProgressBarConfig(),
+ onToggleFullScreen: (isFullScreen) async {
+ if (isFullScreen) {
+ await MyDevice.setAllOrientations();
+ } else {
+ await MyDevice.setPortrait();
+ }
+ },
+ ),
+ );
+ }
+}
diff --git a/lib/features/download/data/datasource/download_datasource.dart b/lib/features/download/data/datasource/download_datasource.dart
index 587574d..843d969 100644
--- a/lib/features/download/data/datasource/download_datasource.dart
+++ b/lib/features/download/data/datasource/download_datasource.dart
@@ -10,8 +10,8 @@ import 'package:hadi_hoda_flutter/core/response/base_response.dart';
import 'package:hadi_hoda_flutter/core/utils/local_storage.dart';
import 'package:hadi_hoda_flutter/core/utils/storage_path.dart';
import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.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/data/model/node_model.dart';
+import 'package:hadi_hoda_flutter/features/level/domain/entity/node_entity.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/total_data_entity.dart';
import 'package:hive/hive.dart';
@@ -146,11 +146,11 @@ class DownloadDatasourceImpl implements IDownloadDatasource {
path: MyApi.levels,
queryParameters: {'lang': selectedLanguage},
);
- final List levels = BaseResponse.getDataList(
- response?['result'],
- (json) => LevelModel.fromJson(json),
+ final List levels = BaseResponse.getDataList(
+ response?['path'],
+ (json) => NodeModel.fromJson(json),
);
- await data.add(TotalDataEntity(code: selectedLanguage, levels: levels));
+ await data.add(TotalDataEntity(code: selectedLanguage, nodes: levels));
}
}
diff --git a/lib/features/guider/data/datasource/guider_datasource.dart b/lib/features/guider/data/datasource/guider_datasource.dart
index b2c2a12..6816cf5 100644
--- a/lib/features/guider/data/datasource/guider_datasource.dart
+++ b/lib/features/guider/data/datasource/guider_datasource.dart
@@ -24,7 +24,7 @@ class GuiderDatasourceImpl implements IGuiderDatasource {
(e) => e.code == selectedLanguage,
orElse: () => TotalDataEntity(),
);
- final LevelEntity? findLevel = findData.levels?.first;
+ final LevelEntity? findLevel = findData.nodes?.first.level;
return findLevel ?? LevelEntity();
} catch (e) {
throw MyException(errorMessage: '$e');
diff --git a/lib/features/home/presentation/bloc/home_bloc.dart b/lib/features/home/presentation/bloc/home_bloc.dart
index 706dfa9..3389b8b 100644
--- a/lib/features/home/presentation/bloc/home_bloc.dart
+++ b/lib/features/home/presentation/bloc/home_bloc.dart
@@ -42,7 +42,7 @@ class HomeBloc extends Bloc {
(e) => e.code == selectedLanguage,
orElse: () => TotalDataEntity(),
);
- if (findData.levels?.isNotEmpty ?? false) {
+ if (findData.nodes?.isNotEmpty ?? false) {
context.goNamed(Routes.levelPage);
} else {
context.goNamed(Routes.downloadPage);
diff --git a/lib/features/level/data/datasource/level_datasource.dart b/lib/features/level/data/datasource/level_datasource.dart
index 64cdcf9..77490c1 100644
--- a/lib/features/level/data/datasource/level_datasource.dart
+++ b/lib/features/level/data/datasource/level_datasource.dart
@@ -2,12 +2,12 @@ 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/params/level_params.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/node_entity.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/total_data_entity.dart';
import 'package:hive/hive.dart';
abstract class ILevelDatasource {
- Future> getLevels({required LevelParams params});
+ Future> getLevels({required LevelParams params});
}
/// Local
@@ -15,7 +15,7 @@ class LocalLevelDatasourceImpl implements ILevelDatasource {
const LocalLevelDatasourceImpl();
@override
- Future> getLevels({required LevelParams params}) async {
+ Future> getLevels({required LevelParams params}) async {
try {
final String selectedLanguage = LocalStorage.readData(
key: MyConstants.selectLanguage) ?? MyConstants.defaultLanguage;
@@ -24,7 +24,7 @@ class LocalLevelDatasourceImpl implements ILevelDatasource {
(e) => e.code == selectedLanguage,
orElse: () => TotalDataEntity(),
);
- return findData.levels ?? [];
+ return findData.nodes ?? [];
} catch (_) {
throw MyException(errorMessage: 'Operation Failed');
}
diff --git a/lib/features/level/data/model/node_model.dart b/lib/features/level/data/model/node_model.dart
new file mode 100644
index 0000000..d476f17
--- /dev/null
+++ b/lib/features/level/data/model/node_model.dart
@@ -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 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,
+ );
+ }
+}
diff --git a/lib/features/level/data/model/prize_model.dart b/lib/features/level/data/model/prize_model.dart
new file mode 100644
index 0000000..7655a37
--- /dev/null
+++ b/lib/features/level/data/model/prize_model.dart
@@ -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 json) {
+ return PrizeModel(
+ id: json['id'],
+ afterLevel: json['after_level'],
+ title: json['title'],
+ imageURL: json['image_url'],
+ animationURL: json['animation_url'],
+ );
+ }
+}
diff --git a/lib/features/level/data/repository_impl/level_repository_impl.dart b/lib/features/level/data/repository_impl/level_repository_impl.dart
index 07d9366..51fdb4f 100644
--- a/lib/features/level/data/repository_impl/level_repository_impl.dart
+++ b/lib/features/level/data/repository_impl/level_repository_impl.dart
@@ -3,7 +3,7 @@ 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/utils/data_state.dart';
import 'package:hadi_hoda_flutter/features/level/data/datasource/level_datasource.dart';
-import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
+import 'package:hadi_hoda_flutter/features/level/domain/entity/node_entity.dart';
import 'package:hadi_hoda_flutter/features/level/domain/repository/level_repository.dart';
class LevelRepositoryImpl implements ILevelRepository {
@@ -12,11 +12,11 @@ class LevelRepositoryImpl implements ILevelRepository {
const LevelRepositoryImpl(this.datasource);
@override
- Future, MyException>> getLevels({
+ Future, MyException>> getLevels({
required LevelParams params,
}) async {
try {
- final List response = await datasource.getLevels(
+ final List response = await datasource.getLevels(
params: params,
);
return DataState.success(response);
diff --git a/lib/features/level/domain/entity/node_entity.dart b/lib/features/level/domain/entity/node_entity.dart
new file mode 100644
index 0000000..6c9fa84
--- /dev/null
+++ b/lib/features/level/domain/entity/node_entity.dart
@@ -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 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});
+}
diff --git a/lib/features/level/domain/entity/node_entity.g.dart b/lib/features/level/domain/entity/node_entity.g.dart
new file mode 100644
index 0000000..41204de
--- /dev/null
+++ b/lib/features/level/domain/entity/node_entity.g.dart
@@ -0,0 +1,86 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'node_entity.dart';
+
+// **************************************************************************
+// TypeAdapterGenerator
+// **************************************************************************
+
+class NodeEntityAdapter extends TypeAdapter {
+ @override
+ final int typeId = 7;
+
+ @override
+ NodeEntity read(BinaryReader reader) {
+ final numOfFields = reader.readByte();
+ final fields = {
+ 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 {
+ @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;
+}
diff --git a/lib/features/level/domain/entity/prize_entity.dart b/lib/features/level/domain/entity/prize_entity.dart
new file mode 100644
index 0000000..7af133b
--- /dev/null
+++ b/lib/features/level/domain/entity/prize_entity.dart
@@ -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,
+ });
+}
diff --git a/lib/features/level/domain/entity/prize_entity.g.dart b/lib/features/level/domain/entity/prize_entity.g.dart
new file mode 100644
index 0000000..5ab0f95
--- /dev/null
+++ b/lib/features/level/domain/entity/prize_entity.g.dart
@@ -0,0 +1,53 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'prize_entity.dart';
+
+// **************************************************************************
+// TypeAdapterGenerator
+// **************************************************************************
+
+class PrizeEntityAdapter extends TypeAdapter {
+ @override
+ final int typeId = 6;
+
+ @override
+ PrizeEntity read(BinaryReader reader) {
+ final numOfFields = reader.readByte();
+ final fields = {
+ 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;
+}
diff --git a/lib/features/level/domain/entity/total_data_entity.dart b/lib/features/level/domain/entity/total_data_entity.dart
index 1428455..4b0c5b8 100644
--- a/lib/features/level/domain/entity/total_data_entity.dart
+++ b/lib/features/level/domain/entity/total_data_entity.dart
@@ -1,4 +1,4 @@
-import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
+import 'package:hadi_hoda_flutter/features/level/domain/entity/node_entity.dart';
import 'package:hive/hive.dart';
part 'total_data_entity.g.dart';
@@ -8,10 +8,10 @@ class TotalDataEntity extends HiveObject{
@HiveField(0)
String? code;
@HiveField(1)
- List? levels;
+ List? nodes;
TotalDataEntity({
this.code,
- this.levels,
+ this.nodes,
});
}
\ No newline at end of file
diff --git a/lib/features/level/domain/entity/total_data_entity.g.dart b/lib/features/level/domain/entity/total_data_entity.g.dart
index 947076d..9512c61 100644
--- a/lib/features/level/domain/entity/total_data_entity.g.dart
+++ b/lib/features/level/domain/entity/total_data_entity.g.dart
@@ -18,7 +18,7 @@ class TotalDataEntityAdapter extends TypeAdapter {
};
return TotalDataEntity(
code: fields[0] as String?,
- levels: (fields[1] as List?)?.cast(),
+ nodes: (fields[1] as List?)?.cast(),
);
}
@@ -29,7 +29,7 @@ class TotalDataEntityAdapter extends TypeAdapter {
..writeByte(0)
..write(obj.code)
..writeByte(1)
- ..write(obj.levels);
+ ..write(obj.nodes);
}
@override
diff --git a/lib/features/level/domain/repository/level_repository.dart b/lib/features/level/domain/repository/level_repository.dart
index 672d6f4..fd29b6b 100644
--- a/lib/features/level/domain/repository/level_repository.dart
+++ b/lib/features/level/domain/repository/level_repository.dart
@@ -1,8 +1,10 @@
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/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 {
- Future, MyException>> getLevels({required LevelParams params});
+ Future, MyException>> getLevels({
+ required LevelParams params,
+ });
}
diff --git a/lib/features/level/domain/usecases/get_levels_usecase.dart b/lib/features/level/domain/usecases/get_levels_usecase.dart
index 3f12106..fe555c0 100644
--- a/lib/features/level/domain/usecases/get_levels_usecase.dart
+++ b/lib/features/level/domain/usecases/get_levels_usecase.dart
@@ -2,16 +2,16 @@ 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/usecase/usecase.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';
import 'package:hadi_hoda_flutter/features/level/domain/repository/level_repository.dart';
-class GetLevelsUseCase implements UseCase, LevelParams> {
+class GetLevelsUseCase implements UseCase, LevelParams> {
final ILevelRepository repository;
const GetLevelsUseCase(this.repository);
@override
- Future, MyException>> call(LevelParams params) {
+ Future, MyException>> call(LevelParams params) {
return repository.getLevels(params: params);
}
}
diff --git a/lib/features/level/presentation/bloc/level_bloc.dart b/lib/features/level/presentation/bloc/level_bloc.dart
index 981cc3e..62449dc 100644
--- a/lib/features/level/presentation/bloc/level_bloc.dart
+++ b/lib/features/level/presentation/bloc/level_bloc.dart
@@ -13,12 +13,15 @@ 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/core/utils/screen_size.dart';
import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart';
+import 'package:hadi_hoda_flutter/core/widgets/dialog/reward_dialog.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_location.dart';
+import 'package:hadi_hoda_flutter/features/level/domain/entity/node_entity.dart';
+import 'package:hadi_hoda_flutter/features/level/domain/entity/prize_entity.dart';
import 'package:hadi_hoda_flutter/features/level/domain/usecases/get_levels_usecase.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_event.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_state.dart';
-import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/level_widget.dart';
+import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/node_widget.dart';
class LevelBloc extends Bloc {
/// ------------constructor------------
@@ -174,7 +177,7 @@ class LevelBloc extends Bloc {
];
- final List levelList = [];
+ final List nodeList = [];
late final Stream volumeStream;
@@ -221,6 +224,14 @@ class LevelBloc extends Bloc {
}
}
+ bool getReward(int index) {
+ final int currentLevel = int.parse(
+ LocalStorage.readData(key: MyConstants.currentLevel) ?? '1',
+ );
+
+ return currentLevel > index;
+ }
+
Future changeMute() async {
await Future.wait([
_mainAudioService.changeMute(),
@@ -235,7 +246,15 @@ class LevelBloc extends Bloc {
return currentLevel - 1;
}
- /// ------------Api Calls------------
+
+ void showReward({
+ required BuildContext context,
+ required PrizeEntity prize,
+ }) {
+ showRewardDialog(context: context, prize: prize);
+ }
+
+ /// ------------Event Calls------------
FutureOr _getLevelListEvent(GetLevelListEvent event,
Emitter emit) async {
final int currentLevel = int.parse(
@@ -244,11 +263,11 @@ class LevelBloc extends Bloc {
await _getLeveslUseCase(LevelParams()).then((value) {
value.fold(
(data) async {
- levelList.addAll(data);
+ nodeList.addAll(data);
try {
emit(state.copyWith(
getLevelStatus: const BaseComplete(''),
- chooseLevel: data.singleWhere((e) => e.order == currentLevel),
+ chooseLevel: data.singleWhere((e) => e.level?.order == currentLevel).level,
));
} catch (e) {
emit(state.copyWith(
diff --git a/lib/features/level/presentation/bloc/level_event.dart b/lib/features/level/presentation/bloc/level_event.dart
index 818ed6f..b5824a6 100644
--- a/lib/features/level/presentation/bloc/level_event.dart
+++ b/lib/features/level/presentation/bloc/level_event.dart
@@ -1,5 +1,5 @@
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
-import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/level_widget.dart';
+import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/node_widget.dart';
sealed class LevelEvent {
const LevelEvent();
diff --git a/lib/features/level/presentation/ui/level_page.dart b/lib/features/level/presentation/ui/level_page.dart
index 2fb4ce7..1c8c075 100644
--- a/lib/features/level/presentation/ui/level_page.dart
+++ b/lib/features/level/presentation/ui/level_page.dart
@@ -16,7 +16,7 @@ import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_event.d
import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_state.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/diamond_level.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/level_path.dart';
-import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/level_widget.dart';
+import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/node_widget.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/play_button.dart';
class LevelPage extends StatelessWidget {
@@ -208,7 +208,7 @@ class LevelPage extends StatelessWidget {
clipBehavior: Clip.none,
children: [
...List.generate(
- context.read().levelList.length,
+ context.read().nodeList.length,
(index) => Positioned(
top: context.read().locationList[index].top,
bottom: context.read().locationList[index].bottom,
@@ -217,10 +217,17 @@ class LevelPage extends StatelessWidget {
child: BlocBuilder(
buildWhen: (previous, current) =>
previous.chooseLevel?.id != current.chooseLevel?.id,
- builder: (context, state) => LevelWidget(
+ builder: (context, state) => NodeWidget(
chooseLevel: state.chooseLevel,
- level: context.read().levelList[index],
- type: context.read().getLevelType(index + 1),
+ node: context.read().nodeList[index],
+ type: context.read().getLevelType,
+ getReward: context.read().getReward,
+ onRewardPressed: (prize) {
+ context.read().showReward(
+ context: context,
+ prize: prize,
+ );
+ },
onTap: (LevelEntity level, LevelType type) {
context.read().add(
ChooseLevelEvent(level, type),
diff --git a/lib/features/level/presentation/ui/widgets/level_widget.dart b/lib/features/level/presentation/ui/widgets/level_widget.dart
deleted file mode 100644
index f39fc81..0000000
--- a/lib/features/level/presentation/ui/widgets/level_widget.dart
+++ /dev/null
@@ -1,121 +0,0 @@
-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_colors.dart';
-import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
-import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart';
-import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart';
-import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
-
-enum LevelType {
- unFinished,
- finished,
- current;
-
- static Map get image => {
- LevelType.unFinished: MyAssets.level,
- LevelType.finished: MyAssets.finishedLevel,
- LevelType.current: MyAssets.currentLevel,
- };
-
- static Map get textShadowColor => {
- LevelType.unFinished: Color(0XFF5B5B5B),
- LevelType.finished: Color(0XFF096D7B),
- LevelType.current: Color(0XFF91500D),
- };
-
- static Map get textColor => {
- LevelType.unFinished: Color(0XFFEDEDED),
- LevelType.finished: Color(0XFFFFF2D0),
- LevelType.current: Color(0XFFFFF2D0),
- };
-}
-
-class LevelWidget extends StatelessWidget {
- const LevelWidget({
- super.key,
- required this.level,
- required this.type,
- required this.chooseLevel,
- this.onTap,
- });
-
- final LevelType type;
- final LevelEntity level;
- final LevelEntity? chooseLevel;
- final Function(LevelEntity level, LevelType type)? onTap;
-
- @override
- Widget build(BuildContext context) {
- return InkWell(
- onTap: () => onTap?.call(level, type),
- child: Stack(
- alignment: Alignment.topCenter,
- clipBehavior: Clip.none,
- children: [
- MyImage(
- image: LevelType.image[type] ?? MyAssets.level,
- fit: BoxFit.cover,
- size: setSize(context: context, tablet: 70, mobile: 44),
- ),
- ShaderMask(
- blendMode: BlendMode.modulate,
- shaderCallback: (bounds) => LinearGradient(
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- colors: [
- Color(0XFFFFFFFF),
- LevelType.textColor[type] ?? MyColors.white,
- ],
- ).createShader(bounds),
- child: Text(
- '${level.order ?? 0}',
- maxLines: 1,
- style: MYTextStyle.button1.copyWith(
- fontSize: setSize(context: context, mobile: 24, tablet: 34),
- shadows: [
- BoxShadow(
- color: LevelType.textShadowColor[type] ?? MyColors.white,
- offset: Offset(0, 2.97),
- )
- ],
- ),
- ),
- ),
- if(level.id == chooseLevel?.id)
- Positioned(
- top: setSize(context: context, mobile: -20, tablet: -30),
- child: MyImage(
- image: MyAssets.location,
- size: setSize(context: context,mobile: 26, tablet: 40),
- ),
- ),
- if(type == LevelType.finished)
- Positioned(
- bottom: 0,
- child: Container(
- height: setSize(context: context, mobile: 17, tablet: 24),
- width: setSize(context: context, mobile: 17, tablet: 24),
- 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),
- ),
- ),
- ],
- ),
- );
- }
-}
diff --git a/lib/features/level/presentation/ui/widgets/node_widget.dart b/lib/features/level/presentation/ui/widgets/node_widget.dart
new file mode 100644
index 0000000..46af3e4
--- /dev/null
+++ b/lib/features/level/presentation/ui/widgets/node_widget.dart
@@ -0,0 +1,161 @@
+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_colors.dart';
+import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
+import 'package:hadi_hoda_flutter/core/utils/set_platform_size.dart';
+import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart';
+import 'package:hadi_hoda_flutter/core/widgets/inkwell/my_inkwell.dart';
+import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
+import 'package:hadi_hoda_flutter/features/level/domain/entity/node_entity.dart';
+import 'package:hadi_hoda_flutter/features/level/domain/entity/prize_entity.dart';
+
+enum LevelType {
+ unFinished,
+ finished,
+ current;
+
+ static Map get image => {
+ LevelType.unFinished: MyAssets.level,
+ LevelType.finished: MyAssets.finishedLevel,
+ LevelType.current: MyAssets.currentLevel,
+ };
+
+ static Map get textShadowColor => {
+ LevelType.unFinished: Color(0XFF5B5B5B),
+ LevelType.finished: Color(0XFF096D7B),
+ LevelType.current: Color(0XFF91500D),
+ };
+
+ static Map get textColor => {
+ LevelType.unFinished: Color(0XFFEDEDED),
+ LevelType.finished: Color(0XFFFFF2D0),
+ LevelType.current: Color(0XFFFFF2D0),
+ };
+}
+
+class NodeWidget extends StatelessWidget {
+ const NodeWidget({
+ super.key,
+ required this.node,
+ required this.getReward,
+ required this.type,
+ required this.chooseLevel,
+ this.onTap,
+ this.onRewardPressed,
+ });
+
+ final LevelType Function(int index) type;
+ final bool Function(int index) getReward;
+ final NodeEntity node;
+ final LevelEntity? chooseLevel;
+ final Function(LevelEntity level, LevelType type)? onTap;
+ final void Function(PrizeEntity prize)? onRewardPressed;
+
+ @override
+ Widget build(BuildContext context) {
+ return Builder(
+ builder: (context) {
+ if (node.nodeType == NodeType.prize) {
+ return MyInkwell(
+ onTap: () {
+ if (getReward(node.prize?.afterLevel ?? 1)) {
+ onRewardPressed?.call(node.prize ?? PrizeEntity());
+ }
+ },
+ child: Stack(
+ alignment: Alignment.center,
+ children: [
+ if (getReward(node.prize?.afterLevel ?? 1)) ...{
+ MyImage(image: MyAssets.giftBackground, size: 70),
+ MyImage(image: MyAssets.gift, size: 50),
+ } else ...{
+ MyImage(image: MyAssets.giftDisable, size: 50),
+ },
+ ],
+ ),
+ );
+ } else {
+ return InkWell(
+ onTap: () => onTap?.call(
+ node.level ?? LevelEntity(),
+ type(node.level?.order ?? 1),
+ ),
+ child: Stack(
+ alignment: Alignment.topCenter,
+ clipBehavior: Clip.none,
+ children: [
+ MyImage(
+ image:
+ LevelType.image[type(node.level?.order ?? 1)] ??
+ MyAssets.level,
+ fit: BoxFit.cover,
+ size: setSize(context: context, tablet: 70, mobile: 44),
+ ),
+ ShaderMask(
+ blendMode: BlendMode.modulate,
+ shaderCallback: (bounds) => LinearGradient(
+ begin: Alignment.topCenter,
+ end: Alignment.bottomCenter,
+ colors: [
+ Color(0XFFFFFFFF),
+ LevelType.textColor[type(node.level?.order ?? 1)] ??
+ MyColors.white,
+ ],
+ ).createShader(bounds),
+ child: Text(
+ '${node.level?.order ?? 0}',
+ maxLines: 1,
+ style: MYTextStyle.button1.copyWith(
+ fontSize: setSize(
+ context: context,
+ mobile: 24,
+ tablet: 34,
+ ),
+ shadows: [
+ BoxShadow(
+ color:
+ LevelType.textShadowColor[type(
+ node.level?.order ?? 1,
+ )] ??
+ MyColors.white,
+ offset: Offset(0, 2.97),
+ ),
+ ],
+ ),
+ ),
+ ),
+ if (node.level?.id == chooseLevel?.id)
+ Positioned(
+ top: setSize(context: context, mobile: -20, tablet: -30),
+ child: MyImage(
+ image: MyAssets.location,
+ size: setSize(context: context, mobile: 26, tablet: 40),
+ ),
+ ),
+ if (type(node.level?.order ?? 1) == LevelType.finished)
+ Positioned(
+ bottom: 0,
+ child: Container(
+ height: setSize(context: context, mobile: 17, tablet: 24),
+ width: setSize(context: context, mobile: 17, tablet: 24),
+ 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),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+ },
+ );
+ }
+}
diff --git a/lib/features/question/data/datasource/question_datasource.dart b/lib/features/question/data/datasource/question_datasource.dart
index 831f844..a89b1f3 100644
--- a/lib/features/question/data/datasource/question_datasource.dart
+++ b/lib/features/question/data/datasource/question_datasource.dart
@@ -3,6 +3,7 @@ import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart';
import 'package:hadi_hoda_flutter/core/params/question_params.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/node_entity.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/total_data_entity.dart';
import 'package:hive/hive.dart';
@@ -25,11 +26,11 @@ class QuestionDatasourceImpl implements IQuestionDatasource {
(e) => e.code == selectedLanguage,
orElse: () => TotalDataEntity(),
);
- final LevelEntity? findLevel = findData.levels?.singleWhere(
- (e) => e.id == params.id,
- orElse: () => LevelEntity(),
+ final NodeEntity? findLevel = findData.nodes?.singleWhere(
+ (e) => e.level?.id == params.id,
+ orElse: () => NodeEntity(),
);
- return findLevel ?? LevelEntity();
+ return findLevel?.level ?? LevelEntity();
} catch (e) {
throw MyException(errorMessage: '$e');
}
@@ -47,14 +48,14 @@ class QuestionDatasourceImpl implements IQuestionDatasource {
(e) => e.code == selectedLanguage,
orElse: () => TotalDataEntity(),
);
- if(index > (findData.levels?.length ?? 0)){
+ if(index > (findData.nodes?.length ?? 0)){
throw MyException();
}
- final LevelEntity? findLevel = findData.levels?.singleWhere(
- (e) => e.order == index,
- orElse: () => LevelEntity(),
+ final NodeEntity? findLevel = findData.nodes?.singleWhere(
+ (e) => e.level?.order == index,
+ orElse: () => NodeEntity(),
);
- return findLevel ?? LevelEntity();
+ return findLevel?.level ?? LevelEntity();
} catch (e) {
throw MyException(errorMessage: '$e');
}
diff --git a/lib/init_bindings.dart b/lib/init_bindings.dart
index c950e18..9a80563 100644
--- a/lib/init_bindings.dart
+++ b/lib/init_bindings.dart
@@ -20,6 +20,8 @@ import 'package:hadi_hoda_flutter/features/guider/domain/usecases/get_first_leve
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/node_entity.dart';
+import 'package:hadi_hoda_flutter/features/level/domain/entity/prize_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';
@@ -92,7 +94,10 @@ Future initDataBase() async {
..registerAdapter(AnswerEntityAdapter())
..registerAdapter(QuestionEntityAdapter())
..registerAdapter(HadithEntityAdapter())
+ ..registerAdapter(PrizeEntityAdapter())
..registerAdapter(LevelEntityAdapter())
+ ..registerAdapter(NodeTypeAdapter())
+ ..registerAdapter(NodeEntityAdapter())
..registerAdapter(TotalDataEntityAdapter());
await Hive.openBox(MyConstants.levelBox);
diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb
index eac3c2f..6999f41 100644
--- a/lib/l10n/app_ar.arb
+++ b/lib/l10n/app_ar.arb
@@ -35,5 +35,6 @@
"showcase_notif": "سيقوم الراوي بقراءة\nالإجابات والخيارات لك.",
"showcase_stepper": "هنا سترى الأسئلة الخاصة\nبهذه المرحلة للوصول\nإلى الماسة.",
"showcase_hadith": "اطّلع على المصادر والأحاديث\nلهذا السؤال",
- "showcase_guide": "هذا دليل سيساعدك\nفي رحلتك."
+ "showcase_guide": "هذا دليل سيساعدك\nفي رحلتك.",
+ "reward": "جائزة"
}
diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb
index 5e2cf47..156cbbd 100644
--- a/lib/l10n/app_de.arb
+++ b/lib/l10n/app_de.arb
@@ -35,5 +35,6 @@
"showcase_notif": "Der Sprecher wird\ndir die Antwortmöglichkeiten\nvorlesen.",
"showcase_stepper": "Hier siehst du die\nFragen für diese\nStufe, um den\nDiamanten zu erreichen.",
"showcase_hadith": "Quellen und\nHadithe zu dieser\nFrage ansehen.",
- "showcase_guide": "Dies ist eine Anleitung,\ndie dir hilft."
+ "showcase_guide": "Dies ist eine Anleitung,\ndie dir hilft.",
+ "reward": "belohnen"
}
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index cdb4691..e4b3a06 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -35,5 +35,6 @@
"showcase_notif": "The announcer will\nread the answers to\nthe options to you.",
"showcase_stepper": "Here you will see the\nquestions for this\nstage to reach the\ndiamond.",
"showcase_hadith": "View sources and\nhadiths for this\nquestion",
- "showcase_guide": "This is a guide that will\nhelp you."
+ "showcase_guide": "This is a guide that will\nhelp you.",
+ "reward": "Reward"
}
\ No newline at end of file
diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb
index 8c008a6..5ba2ce2 100644
--- a/lib/l10n/app_fr.arb
+++ b/lib/l10n/app_fr.arb
@@ -35,5 +35,6 @@
"showcase_notif": "Le narrateur lira\nles réponses des\noptions pour toi.",
"showcase_stepper": "Ici, tu verras les\nquestions de cette\nétape pour atteindre\nle diamant.",
"showcase_hadith": "Consulte les sources et\nles hadiths pour cette\nquestion",
- "showcase_guide": "Ceci est un guide\nqui t’aidera."
+ "showcase_guide": "Ceci est un guide\nqui t’aidera.",
+ "reward": "récompense"
}
diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart
index dce9f70..68eec8f 100644
--- a/lib/l10n/app_localizations.dart
+++ b/lib/l10n/app_localizations.dart
@@ -321,6 +321,12 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'This is a guide that will\nhelp you.'**
String get showcase_guide;
+
+ /// No description provided for @reward.
+ ///
+ /// In en, this message translates to:
+ /// **'Reward'**
+ String get reward;
}
class _AppLocalizationsDelegate
diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart
index fdbe46b..3c5f345 100644
--- a/lib/l10n/app_localizations_ar.dart
+++ b/lib/l10n/app_localizations_ar.dart
@@ -119,4 +119,7 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get showcase_guide => 'هذا دليل سيساعدك\nفي رحلتك.';
+
+ @override
+ String get reward => 'جائزة';
}
diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart
index bc7873b..49f7a1f 100644
--- a/lib/l10n/app_localizations_de.dart
+++ b/lib/l10n/app_localizations_de.dart
@@ -126,4 +126,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get showcase_guide => 'Dies ist eine Anleitung,\ndie dir hilft.';
+
+ @override
+ String get reward => 'belohnen';
}
diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart
index baf9d51..991362a 100644
--- a/lib/l10n/app_localizations_en.dart
+++ b/lib/l10n/app_localizations_en.dart
@@ -123,4 +123,7 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get showcase_guide => 'This is a guide that will\nhelp you.';
+
+ @override
+ String get reward => 'Reward';
}
diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart
index f870a7e..6e6ec23 100644
--- a/lib/l10n/app_localizations_fr.dart
+++ b/lib/l10n/app_localizations_fr.dart
@@ -124,4 +124,7 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get showcase_guide => 'Ceci est un guide\nqui t’aidera.';
+
+ @override
+ String get reward => 'récompense';
}
diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart
index dbd16a8..e374e4a 100644
--- a/lib/l10n/app_localizations_ru.dart
+++ b/lib/l10n/app_localizations_ru.dart
@@ -124,4 +124,7 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get showcase_guide => 'Это руководство,\nкоторое поможет тебе.';
+
+ @override
+ String get reward => 'награда';
}
diff --git a/lib/l10n/app_localizations_tr.dart b/lib/l10n/app_localizations_tr.dart
index 0ae805a..db16200 100644
--- a/lib/l10n/app_localizations_tr.dart
+++ b/lib/l10n/app_localizations_tr.dart
@@ -123,4 +123,7 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get showcase_guide => 'Bu, sana yardımcı olacak\nbir rehberdir.';
+
+ @override
+ String get reward => 'ödül';
}
diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb
index c457456..9460133 100644
--- a/lib/l10n/app_ru.arb
+++ b/lib/l10n/app_ru.arb
@@ -35,5 +35,6 @@
"showcase_notif": "Диктор прочитает\nтебе все варианты\nответов.",
"showcase_stepper": "Здесь ты увидишь\nвопросы этого этапа,\nчтобы получить\nалмаз.",
"showcase_hadith": "Просмотри источники и\nхадисы по этому вопросу",
- "showcase_guide": "Это руководство,\nкоторое поможет тебе."
+ "showcase_guide": "Это руководство,\nкоторое поможет тебе.",
+ "reward": "награда"
}
diff --git a/lib/l10n/app_tr.arb b/lib/l10n/app_tr.arb
index e8b78c8..9e4691c 100644
--- a/lib/l10n/app_tr.arb
+++ b/lib/l10n/app_tr.arb
@@ -35,5 +35,6 @@
"showcase_notif": "Anlatıcı, seçeneklerin\ncevaplarını sana\nokuyacak.",
"showcase_stepper": "Burada, elmasa ulaşmak\niçin bu aşamadaki\nsoruları göreceksin.",
"showcase_hadith": "Bu soruya ait\nkaynakları ve hadisleri\nincele",
- "showcase_guide": "Bu, sana yardımcı olacak\nbir rehberdir."
+ "showcase_guide": "Bu, sana yardımcı olacak\nbir rehberdir.",
+ "reward": "ödül"
}
diff --git a/pubspec.lock b/pubspec.lock
index f14a07c..b4f8cb6 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -145,6 +145,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
+ charcode:
+ dependency: transitive
+ description:
+ name: charcode
+ sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.0"
checked_yaml:
dependency: transitive
description:
@@ -201,6 +209,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.6"
+ csslib:
+ dependency: transitive
+ description:
+ name: csslib
+ sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.2"
dart_style:
dependency: transitive
description:
@@ -209,6 +225,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.6"
+ dbus:
+ dependency: transitive
+ description:
+ name: dbus
+ sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.7.11"
dio:
dependency: "direct main"
description:
@@ -317,6 +341,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ freezed_annotation:
+ dependency: transitive
+ description:
+ name: freezed_annotation
+ sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.0"
frontend_server_client:
dependency: transitive
description:
@@ -325,6 +357,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.0"
+ get:
+ dependency: transitive
+ description:
+ name: get
+ sha256: "5ed34a7925b85336e15d472cc4cfe7d9ebf4ab8e8b9f688585bf6b50f4c3d79a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.7.3"
get_it:
dependency: "direct main"
description:
@@ -381,6 +421,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.1"
+ html:
+ dependency: transitive
+ description:
+ name: html
+ sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.15.6"
http:
dependency: transitive
description:
@@ -557,6 +605,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.0"
+ package_info_plus:
+ dependency: transitive
+ description:
+ name: package_info_plus
+ sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.0.0"
+ package_info_plus_platform_interface:
+ dependency: transitive
+ description:
+ name: package_info_plus_platform_interface
+ sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.2.1"
path:
dependency: transitive
description:
@@ -653,6 +717,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.8"
+ pod_player:
+ dependency: "direct main"
+ description:
+ name: pod_player
+ sha256: "38564f2e0d9b06fa9e4ad5023be2f6fa4cd9f95f70ace2f517a6d899b166a502"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.2.2"
pool:
dependency: transitive
description:
@@ -789,6 +861,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.0.1"
+ simple_sparse_list:
+ dependency: transitive
+ description:
+ name: simple_sparse_list
+ sha256: aa648fd240fa39b49dcd11c19c266990006006de6699a412de485695910fbc1f
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.1.4"
sky_engine:
dependency: transitive
description: flutter
@@ -898,6 +978,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
+ unicode:
+ dependency: transitive
+ description:
+ name: unicode
+ sha256: "0d99edbd2e74726bed2e4989713c8bec02e5581628e334d8c88c0271593fb402"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.8"
+ universal_html:
+ dependency: transitive
+ description:
+ name: universal_html
+ sha256: c0bcae5c733c60f26c7dfc88b10b0fd27cbcc45cb7492311cdaa6067e21c9cd4
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.0"
+ universal_io:
+ dependency: transitive
+ description:
+ name: universal_io
+ sha256: f63cbc48103236abf48e345e07a03ce5757ea86285ed313a6a032596ed9301e2
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.1"
uuid:
dependency: transitive
description:
@@ -938,6 +1042,46 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.2.0"
+ video_player:
+ dependency: transitive
+ description:
+ name: video_player
+ sha256: "096bc28ce10d131be80dfb00c223024eb0fba301315a406728ab43dd99c45bdf"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.10.1"
+ video_player_android:
+ dependency: transitive
+ description:
+ name: video_player_android
+ sha256: d74b66f283afff135d5be0ceccca2ca74dff7df1e9b1eaca6bd4699875d3ae60
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.8.22"
+ video_player_avfoundation:
+ dependency: transitive
+ description:
+ name: video_player_avfoundation
+ sha256: e4d33b79a064498c6eb3a6a492b6a5012573d4943c28d566caf1a6c0840fe78d
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.8.8"
+ video_player_platform_interface:
+ dependency: transitive
+ description:
+ name: video_player_platform_interface
+ sha256: "57c5d73173f76d801129d0531c2774052c5a7c11ccb962f1830630decd9f24ec"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.6.0"
+ video_player_web:
+ dependency: transitive
+ description:
+ name: video_player_web
+ sha256: "9f3c00be2ef9b76a95d94ac5119fb843dca6f2c69e6c9968f6f2b6c9e7afbdeb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.0"
vm_service:
dependency: transitive
description:
@@ -946,6 +1090,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "15.0.2"
+ wakelock_plus:
+ dependency: transitive
+ description:
+ name: wakelock_plus
+ sha256: "9296d40c9adbedaba95d1e704f4e0b434be446e2792948d0e4aa977048104228"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.0"
+ wakelock_plus_platform_interface:
+ dependency: transitive
+ description:
+ name: wakelock_plus_platform_interface
+ sha256: "036deb14cd62f558ca3b73006d52ce049fabcdcb2eddfe0bf0fe4e8a943b5cf2"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.0"
watcher:
dependency: transitive
description:
@@ -978,6 +1138,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.3"
+ win32:
+ dependency: transitive
+ description:
+ name: win32
+ sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.15.0"
xdg_directories:
dependency: transitive
description:
@@ -1002,6 +1170,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.3"
+ youtube_explode_dart:
+ dependency: transitive
+ description:
+ name: youtube_explode_dart
+ sha256: "947ba05e0c4f050743e480e7bca3575ff6427d86cc898c1a69f5e1d188cdc9e0"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.5.3"
sdks:
dart: ">=3.9.2 <4.0.0"
flutter: ">=3.35.0"
diff --git a/pubspec.yaml b/pubspec.yaml
index 98a157d..f8ba2c6 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -28,6 +28,7 @@ dependencies:
lottie: ^3.3.2
path_drawing: ^1.0.1
path_provider: ^2.1.5
+ pod_player: ^0.2.2
pretty_dio_logger: ^1.4.0
shared_preferences: ^2.5.3
showcaseview: ^5.0.1