diff --git a/assets/videos/intro_1.mp4 b/assets/videos/intro_1.mp4 deleted file mode 100644 index d4e82b4..0000000 Binary files a/assets/videos/intro_1.mp4 and /dev/null differ diff --git a/assets/videos/intro_1_ar.mp4 b/assets/videos/intro_1_ar.mp4 new file mode 100644 index 0000000..6eaa64c Binary files /dev/null and b/assets/videos/intro_1_ar.mp4 differ diff --git a/assets/videos/intro_1_en.mp4 b/assets/videos/intro_1_en.mp4 new file mode 100644 index 0000000..938ac7c Binary files /dev/null and b/assets/videos/intro_1_en.mp4 differ diff --git a/assets/videos/intro_2.mp4 b/assets/videos/intro_2.mp4 deleted file mode 100644 index 37676a5..0000000 Binary files a/assets/videos/intro_2.mp4 and /dev/null differ diff --git a/assets/videos/intro_2_ar.mp4 b/assets/videos/intro_2_ar.mp4 new file mode 100644 index 0000000..9dc15a9 Binary files /dev/null and b/assets/videos/intro_2_ar.mp4 differ diff --git a/assets/videos/intro_2_en.mp4 b/assets/videos/intro_2_en.mp4 new file mode 100644 index 0000000..9dea852 Binary files /dev/null and b/assets/videos/intro_2_en.mp4 differ diff --git a/assets/videos/intro_3.mp4 b/assets/videos/intro_3.mp4 deleted file mode 100644 index 56f10d9..0000000 Binary files a/assets/videos/intro_3.mp4 and /dev/null differ diff --git a/assets/videos/intro_3_ar.mp4 b/assets/videos/intro_3_ar.mp4 new file mode 100644 index 0000000..9f0fa93 Binary files /dev/null and b/assets/videos/intro_3_ar.mp4 differ diff --git a/assets/videos/intro_3_en.mp4 b/assets/videos/intro_3_en.mp4 new file mode 100644 index 0000000..89b42c5 Binary files /dev/null and b/assets/videos/intro_3_en.mp4 differ diff --git a/assets/videos/intro_4.mp4 b/assets/videos/intro_4.mp4 deleted file mode 100644 index 1b7df80..0000000 Binary files a/assets/videos/intro_4.mp4 and /dev/null differ diff --git a/assets/videos/intro_4_ar.mp4 b/assets/videos/intro_4_ar.mp4 new file mode 100644 index 0000000..c88e2d4 Binary files /dev/null and b/assets/videos/intro_4_ar.mp4 differ diff --git a/assets/videos/intro_4_en.mp4 b/assets/videos/intro_4_en.mp4 new file mode 100644 index 0000000..5925b35 Binary files /dev/null and b/assets/videos/intro_4_en.mp4 differ diff --git a/assets/videos/intro_5.mp4 b/assets/videos/intro_5.mp4 deleted file mode 100644 index c456f63..0000000 Binary files a/assets/videos/intro_5.mp4 and /dev/null differ diff --git a/assets/videos/intro_5_ar.mp4 b/assets/videos/intro_5_ar.mp4 new file mode 100644 index 0000000..d8b3f1e Binary files /dev/null and b/assets/videos/intro_5_ar.mp4 differ diff --git a/assets/videos/intro_5_en.mp4 b/assets/videos/intro_5_en.mp4 new file mode 100644 index 0000000..c2cf05e Binary files /dev/null and b/assets/videos/intro_5_en.mp4 differ diff --git a/lib/core/widgets/answer_box/answer_box.dart b/lib/core/widgets/answer_box/answer_box.dart index 7fc8689..27fb0fc 100644 --- a/lib/core/widgets/answer_box/answer_box.dart +++ b/lib/core/widgets/answer_box/answer_box.dart @@ -35,7 +35,7 @@ class _AnswerBoxState extends State { @override Widget build(BuildContext context) { - return Hero( + return Hero( tag: 'Hero_answer_${widget.answer.id}', child: Material( type: MaterialType.transparency, @@ -68,29 +68,33 @@ class _AnswerBoxState extends State { left: 0, right: 0, bottom: 0, - child: AnswerTextBox(text: widget.answer.title ?? ''), - ), - if(widget.answer.audioInfo?.filename?.isNotEmpty == true)Positioned( - top: setSize( - context: context, - mobile: MySpaces.s12, - tablet: MySpaces.s20, - ), - right: setSize( - context: context, - mobile: MySpaces.s8, - tablet: MySpaces.s20, + child: AnswerTextBox( + text: widget.answer.title ?? '', + padding: const EdgeInsetsGeometry.all(10), ), - child: GestureDetector( - onTap: () { - widget.onNotifTap?.call(widget.answer); - }, - child: MyImage( - image: MyAssets.iconNotif, - size: setSize(context: context, tablet: 50), + ), + if (widget.answer.audioInfo?.filename?.isNotEmpty == true) + Positioned( + top: setSize( + context: context, + mobile: MySpaces.s12, + tablet: MySpaces.s20, + ), + right: setSize( + context: context, + mobile: MySpaces.s8, + tablet: MySpaces.s20, + ), + child: GestureDetector( + onTap: () { + widget.onNotifTap?.call(widget.answer); + }, + child: MyImage( + image: MyAssets.iconNotif, + size: setSize(context: context, tablet: 50), + ), ), ), - ), ], ), ), diff --git a/lib/core/widgets/answer_box/answer_box_show.dart b/lib/core/widgets/answer_box/answer_box_show.dart index 658def6..94c5a93 100644 --- a/lib/core/widgets/answer_box/answer_box_show.dart +++ b/lib/core/widgets/answer_box/answer_box_show.dart @@ -46,7 +46,8 @@ class AnswerBoxShow extends StatelessWidget { padding: const EdgeInsetsGeometry.all(14), ), ), - Positioned( + if (answer.audioInfo?.filename?.isNotEmpty == true) + Positioned( top: setSize(context: context, mobile: MySpaces.s30, tablet: 60), right: setSize( context: context, diff --git a/lib/features/intro/presentation/bloc/intro_bloc.dart b/lib/features/intro/presentation/bloc/intro_bloc.dart index ae5acdc..a5ed4b2 100644 --- a/lib/features/intro/presentation/bloc/intro_bloc.dart +++ b/lib/features/intro/presentation/bloc/intro_bloc.dart @@ -45,22 +45,24 @@ class IntroBloc extends Bloc { Intro4Screen(key: Key('3')), Intro5Screen(key: Key('4')), ]; - + late final _supportedIntroLangs = const ['en', 'ar']; + late final _lang = LocalStorage.readData(key: MyConstants.selectLanguage) ?? MyConstants.defaultLanguage; + late final _introLang = _supportedIntroLangs.contains(_lang) ? _lang : MyConstants.defaultLanguage; /// ------------Controllers------------ - final PodPlayerController podController1 = PodPlayerController( - playVideoFrom: PlayVideoFrom.asset('assets/videos/intro_1.mp4'), + late final PodPlayerController podController1 = PodPlayerController( + playVideoFrom: PlayVideoFrom.asset('assets/videos/intro_1_$_introLang.mp4'), ); - final PodPlayerController podController2 = PodPlayerController( - playVideoFrom: PlayVideoFrom.asset('assets/videos/intro_2.mp4'), + late final PodPlayerController podController2 = PodPlayerController( + playVideoFrom: PlayVideoFrom.asset('assets/videos/intro_2_$_introLang.mp4'), ); - final PodPlayerController podController3 = PodPlayerController( - playVideoFrom: PlayVideoFrom.asset('assets/videos/intro_3.mp4'), + late final PodPlayerController podController3 = PodPlayerController( + playVideoFrom: PlayVideoFrom.asset('assets/videos/intro_3_$_introLang.mp4'), ); - final PodPlayerController podController4 = PodPlayerController( - playVideoFrom: PlayVideoFrom.asset('assets/videos/intro_4.mp4'), + late final PodPlayerController podController4 = PodPlayerController( + playVideoFrom: PlayVideoFrom.asset('assets/videos/intro_4_$_introLang.mp4'), ); - final PodPlayerController podController5 = PodPlayerController( - playVideoFrom: PlayVideoFrom.asset('assets/videos/intro_5.mp4'), + late final PodPlayerController podController5 = PodPlayerController( + playVideoFrom: PlayVideoFrom.asset('assets/videos/intro_5_$_introLang.mp4'), ); /// ------------Functions------------ diff --git a/lib/features/level/presentation/bloc/level_bloc.dart b/lib/features/level/presentation/bloc/level_bloc.dart index 90eb0c5..410ebe2 100644 --- a/lib/features/level/presentation/bloc/level_bloc.dart +++ b/lib/features/level/presentation/bloc/level_bloc.dart @@ -117,71 +117,78 @@ class LevelBloc extends Bloc { left: setSize(context: MyContext.get, mobile: 0.15.w, tablet: 0.58.w), index: 11, ), + ////// LevelLocation( bottom: setSize(context: MyContext.get, mobile: 0.78.h, tablet: 1.52.h), left: setSize(context: MyContext.get, mobile: 0.4.w, tablet: 0.45.w), index: 12, ), + ///// + LevelLocation( + bottom: setSize(context: MyContext.get, mobile: 0.78.h, tablet: 1.52.h), + left: setSize(context: MyContext.get, mobile: 0.35.w, tablet: 0.55.w), + index: 13, + ), LevelLocation( - bottom: setSize(context: MyContext.get, mobile: 0.85.h, tablet: 1.68.h), - left: setSize(context: MyContext.get, mobile: 0.5.w, tablet: 0.68.w), - index: 13, - ), - LevelLocation( - bottom: setSize(context: MyContext.get, mobile: 0.94.h, tablet: 1.8.h), - left: setSize(context: MyContext.get, mobile: 0.45.w, tablet: 0.6.w), + bottom: setSize(context: MyContext.get, mobile: 0.85.h, tablet: 1.62.h), + left: setSize(context: MyContext.get, mobile: 0.5.w, tablet: 0.46.w), index: 14, ), LevelLocation( - bottom: setSize(context: MyContext.get, mobile: 1.02.h, tablet: 1.95.h), - left: setSize(context: MyContext.get, mobile: 0.45.w, tablet: 0.6.w), + bottom: setSize(context: MyContext.get, mobile: 0.94.h, tablet: 1.62.h), + left: setSize(context: MyContext.get, mobile: 0.45.w, tablet: 0.3.w), index: 15, ), LevelLocation( - bottom: setSize(context: MyContext.get, mobile: 1.02.h, tablet: 2.01.h), - left: setSize(context: MyContext.get, mobile: 0.2.w, tablet: 0.4.w), + bottom: setSize(context: MyContext.get, mobile: 1.02.h, tablet: 1.62.h), + left: setSize(context: MyContext.get, mobile: 0.45.w, tablet: 0.11.w), index: 16, ), LevelLocation( - bottom: setSize(context: MyContext.get, mobile: 1.02.h, tablet: 2.0.h), - left: setSize(context: MyContext.get, mobile: 0, tablet: 0.1.w), + bottom: setSize(context: MyContext.get, mobile: 1.02.h, tablet: 1.7.h), + left: setSize(context: MyContext.get, mobile: 0.2.w, tablet: 0.0.w), index: 17, ), LevelLocation( - bottom: setSize(context: MyContext.get, mobile: 1.1.h, tablet: 2.17.h), - left: setSize(context: MyContext.get, mobile: 0, tablet: 0.04.w), + bottom: setSize(context: MyContext.get, mobile: 1.02.h, tablet: 1.825.h), + left: setSize(context: MyContext.get, mobile: 0, tablet: 0.0.w), index: 18, ), LevelLocation( - bottom: setSize(context: MyContext.get, mobile: 1.2.h, tablet: 2.32.h), - left: setSize(context: MyContext.get, mobile: 0.05.w, tablet: 0.1.w), + bottom: setSize(context: MyContext.get, mobile: 1.1.h, tablet: 1.88.h), + left: setSize(context: MyContext.get, mobile: 0, tablet: 0.125.w), index: 19, ), LevelLocation( - bottom: setSize(context: MyContext.get, mobile: 1.23.h, tablet: 2.35.h), - left: setSize(context: MyContext.get, mobile: 0.23.w, tablet: 0.25.w), + bottom: setSize(context: MyContext.get, mobile: 1.19.h, tablet: 1.97.h), + left: setSize(context: MyContext.get, mobile: 0.05.w, tablet: 0.289.w), index: 20, ), LevelLocation( - bottom: setSize(context: MyContext.get, mobile: 1.31.h, tablet: 2.5.h), - left: setSize(context: MyContext.get, mobile: 0.2.w, tablet: 0.33.w), + bottom: setSize(context: MyContext.get, mobile: 1.23.h, tablet: 2.10.h), + left: setSize(context: MyContext.get, mobile: 0.23.w, tablet: 0.21.w), index: 21, ), LevelLocation( - bottom: setSize(context: MyContext.get, mobile: 1.37.h, tablet: 2.6.h), - left: setSize(context: MyContext.get, mobile: 0.1.w, tablet: 0.2.w), + bottom: setSize(context: MyContext.get, mobile: 1.31.h, tablet: 2.169.h), + left: setSize(context: MyContext.get, mobile: 0.2.w, tablet: 0.1.w), index: 22, ), LevelLocation( - bottom: setSize(context: MyContext.get, mobile: 1.45.h, tablet: 2.7.h), + bottom: setSize(context: MyContext.get, mobile: 1.37.h, tablet: 2.25.h), left: setSize(context: MyContext.get, mobile: 0.1.w, tablet: 0.1.w), index: 23, ), LevelLocation( - bottom: setSize(context: MyContext.get, mobile: 1.5.h, tablet: 2.8.h), - left: setSize(context: MyContext.get, mobile: 0.2.w, tablet: 0.15.w), + bottom: setSize(context: MyContext.get, mobile: 1.45.h, tablet: 2.34.h), + left: setSize(context: MyContext.get, mobile: 0.12.w, tablet: 0.225.w), index: 24, ), + // LevelLocation( + // bottom: setSize(context: MyContext.get, mobile: 1.5.h, tablet: 2.8.h), + // left: setSize(context: MyContext.get, mobile: 0.2.w, tablet: 0.15.w), + // index: 24, + // ), ]; final List nodeList = []; diff --git a/lib/features/level/presentation/ui/level_page.dart b/lib/features/level/presentation/ui/level_page.dart index 4a00d56..1c5c920 100644 --- a/lib/features/level/presentation/ui/level_page.dart +++ b/lib/features/level/presentation/ui/level_page.dart @@ -1,3 +1,4 @@ +import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -27,11 +28,13 @@ import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/level_p 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'; +import '../../domain/entity/level_location.dart'; + class LevelPage extends StatefulWidget { - const LevelPage({super.key}); + const LevelPage({super.key}); - @override - State createState() => _LevelPageState(); + @override + State createState() => _LevelPageState(); } class _LevelPageState extends State { @@ -40,18 +43,24 @@ class _LevelPageState extends State { @override void initState() { super.initState(); - if(!kDebugMode)_triggerRemainingLevelsDownload(); + if (!kDebugMode) _triggerRemainingLevelsDownload(); } void _triggerRemainingLevelsDownload() { WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; - final maxLevelCount = int.tryParse(LocalStorage.readData(key: MyConstants.maxLevelCount) ?? '20') ?? 20; - context.read().add(StartDownloadEvent(toLevel: maxLevelCount)); + final maxLevelCount = + int.tryParse( + LocalStorage.readData(key: MyConstants.maxLevelCount) ?? '20', + ) ?? + 20; + context.read().add( + StartDownloadEvent(toLevel: maxLevelCount), + ); }); } - @override + @override Widget build(BuildContext context) { return Scaffold( body: MyPopScope( @@ -68,7 +77,6 @@ class _LevelPageState extends State { _background(context), _planets(context), _path(context), - ], ), ), @@ -78,12 +86,9 @@ class _LevelPageState extends State { child: Stack( alignment: Alignment.center, clipBehavior: Clip.none, - children: [ - _ship(context), - _playButton(context), - ], + children: [_ship(context), _playButton(context)], ), - ) + ), ], ), ), @@ -214,94 +219,126 @@ class _LevelPageState extends State { } Widget _path(BuildContext context) { - return Positioned.fill( - top: 250, - bottom: 150, - right: 50, - left: 50, - child: Stack( - alignment: Alignment.center, - children: [ - const Positioned.fill( - child: LevelPath(), + return BlocBuilder( + builder: (context, state) { + final locationList = context.read().locationList; + final nodeList = context.read().nodeList; + final comingSoon = locationList.firstWhereIndexedOrNull(( + index, + location, + ) { + final node = nodeList.elementAtOrNull(index); + return node?.nodeType == NodeType.comingSoon; + }); + return Positioned.fill( + child: Stack( + children: [ + Positioned.fill( + top: 250, + bottom: 150, + right: 50, + left: 50, + child: Stack( + alignment: Alignment.center, + children: [ + const Positioned.fill(child: LevelPath()), + Positioned.fill( + child: _levelLocation( + locationList: locationList, + nodeList: nodeList, + ), + ), + ], + ), + ), + if (comingSoon != null) ...[ + Positioned( + top: 0, + right: 0, + left: 0, + bottom: (comingSoon.bottom ?? 0) + 190, + child: _lockMapShadowCover(), + ), + Positioned( + left: 50, + right: 50, + bottom: (comingSoon.bottom ?? 0) + 195, + child: const ComingSoonLevel(), + ), + ], + ], ), - Positioned.fill(child: _levelLocation(context)), - ], - ), + ); + }, ); } - Widget _levelLocation(BuildContext context) { - return BlocBuilder( - builder: (context, state) => Stack( - clipBehavior: Clip.none, - children: [ - ...List.generate( - context.read().nodeList.length, - (index) { - final node = context.read().nodeList[index]; - if(node.nodeType == NodeType.comingSoon) { - if(node.nodeType == NodeType.comingSoon) { - return Positioned( - left: 0, - right: 0, - bottom: (context.read().locationList[index].bottom ?? 0) + 88, - child: const ComingSoonLevel()); - } - } - return Positioned( - top: context.read().locationList[index].top, - bottom: context.read().locationList[index].bottom, - right: context.read().locationList[index].right, - left: context.read().locationList[index].left, - child: BlocBuilder( - buildWhen: (previous, current) => - previous.chooseLevel?.id != current.chooseLevel?.id, - builder: (context, state) => NodeWidget( - chooseLevel: state.chooseLevel, - 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), - ); - }, - ), - ), - ); - }, - ), - ], - ), - ); - } + Widget _levelLocation({ + required List locationList, + required List nodeList, + }) { + return BlocBuilder( + builder: (context, state) { + return Stack( + clipBehavior: Clip.none, + children: [ + ...List.generate(locationList.length, (index) { + final node = nodeList.elementAtOrNull(index); + final location = locationList[index]; + if (node?.nodeType == NodeType.comingSoon) + return const SizedBox(); + return Positioned( + top: location.top, + bottom: location.bottom, + right: location.right, + left: location.left, + child: BlocBuilder( + buildWhen: (previous, current) => + previous.chooseLevel?.id != current.chooseLevel?.id, + builder: (context, state) => NodeWidget( + chooseLevel: state.chooseLevel, + levelIndex: location.index, + node: node, + 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), + ); + }, + ), + ), + ); + }), + ], + ); + }, + ); + } - Widget _ship(BuildContext context) { - return const ShipAnim( - child: MyImage(image: MyAssets.ship), - ); - } + Widget _ship(BuildContext context) { + return const ShipAnim(child: MyImage(image: MyAssets.ship)); + } - Widget _playButton(BuildContext context) { - return BlocBuilder( - buildWhen: (previous, current) => - previous.chooseLevel?.id != current.chooseLevel?.id, - builder: (context, state) { - return PlayButton( - level: state.chooseLevel ?? LevelEntity(), - onTap: (level) => - context.read().goToQuestionPage(context, level), - ); - } - ); - } + Widget _playButton(BuildContext context) { + return BlocBuilder( + buildWhen: (previous, current) => + previous.chooseLevel?.id != current.chooseLevel?.id, + builder: (context, state) { + return PlayButton( + level: state.chooseLevel ?? LevelEntity(), + onTap: (level) => + context.read().goToQuestionPage(context, level), + ); + }, + ); + } Positioned _topSection(BuildContext context) { return Positioned( @@ -322,9 +359,7 @@ class _LevelPageState extends State { ), ), const Spacer(), - DiamondLevel( - diamonds: context.read().diamonds, - ), + DiamondLevel(diamonds: context.read().diamonds), StreamBuilder( initialData: 1, stream: context.read().volumeStream, @@ -364,9 +399,26 @@ class _LevelPageState extends State { // } Widget _background(BuildContext context) { - return const MyImage( - image: MyAssets.mapBackground, - fit: BoxFit.cover, + return const MyImage(image: MyAssets.mapBackground, fit: BoxFit.cover); + } + + Widget _lockMapShadowCover() { + return Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Colors.black.withValues(alpha: .8), + Colors.black54, + Colors.black54, + Colors.black54, + Colors.black54, + Colors.black54, + Colors.transparent, + ], + begin: AlignmentGeometry.topCenter, + end: AlignmentGeometry.bottomCenter, + ), + ), ); } } diff --git a/lib/features/level/presentation/ui/widgets/node_widget.dart b/lib/features/level/presentation/ui/widgets/node_widget.dart index 0750bb3..6061094 100644 --- a/lib/features/level/presentation/ui/widgets/node_widget.dart +++ b/lib/features/level/presentation/ui/widgets/node_widget.dart @@ -42,150 +42,157 @@ class NodeWidget extends StatelessWidget { required this.getReward, required this.type, required this.chooseLevel, + required this.levelIndex, this.onTap, this.onRewardPressed, }); final LevelType Function(int index) type; final bool Function(int index) getReward; - final NodeEntity node; + final NodeEntity? node; + final int? levelIndex; 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.comingSoon) { - return const ComingSoonLevel(); - } else 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)) ...{ - const MyImage(image: MyAssets.giftBackground, size: 70), - const MyImage(image: MyAssets.gift, size: 50), - } else ...{ - const MyImage(image: MyAssets.giftDisable, size: 50), + final levelIsInComingSoon = node == null || node?.nodeType == NodeType.comingSoon; + return AbsorbPointer( + absorbing: levelIsInComingSoon, + child: Opacity( + opacity: levelIsInComingSoon ? .85 : 1, + child: Builder( + builder: (context) { + if (node?.nodeType == NodeType.prize) { + return MyInkwell( + onTap: () { + if (getReward(node?.prize?.afterLevel ?? 1)) { + onRewardPressed?.call(node!.prize ?? PrizeEntity()); + } }, - ], - ), - ); - } else { - return Transform.translate( - offset: const Offset(-8, 8), - child: InkWell( - onTap: () => onTap?.call( - node.level ?? LevelEntity(), - type(node.level?.order ?? 1), - ), - child: Container( - color: Colors.transparent, - padding: const EdgeInsets.all(8), child: Stack( - alignment: Alignment.topCenter, - clipBehavior: Clip.none, + alignment: Alignment.center, 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: [ - const 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, - )] ?? + if (getReward(node?.prize?.afterLevel ?? 1)) ...{ + const MyImage(image: MyAssets.giftBackground, size: 70), + const MyImage(image: MyAssets.gift, size: 50), + } else ...{ + const MyImage(image: MyAssets.giftDisable, size: 50), + }, + ], + ), + ); + } else { + return Transform.translate( + offset: const Offset(-8, 8), + child: InkWell( + onTap: () => onTap?.call( + node?.level ?? LevelEntity(), + type(node?.level?.order ?? levelIndex ?? 1), + ), + child: Container( + color: Colors.transparent, + padding: const EdgeInsets.all(8), + child: Stack( + alignment: Alignment.topCenter, + clipBehavior: Clip.none, + children: [ + MyImage( + image: + LevelType.image[type(node?.level?.order ?? levelIndex ?? 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: [ + const Color(0XFFFFFFFF), + LevelType.textColor[type(node?.level?.order ?? levelIndex ?? 1)] ?? MyColors.white, - offset: const Offset(0, 2.97), + ], + ).createShader(bounds), + child: Text( + '${node?.level?.order ?? levelIndex ?? 0}', + maxLines: 1, + style: MYTextStyle.button1.copyWith( + fontSize: setSize( + context: context, + mobile: 24, + tablet: 34, + ), + shadows: [ + BoxShadow( + color: + LevelType.textShadowColor[type( + node?.level?.order ?? levelIndex ?? 1, + )] ?? + MyColors.white, + offset: const 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: const EdgeInsets.all(3), - decoration: BoxDecoration( - shape: BoxShape.circle, - border: Border.all( - width: 1, - color: const Color(0XFF3CFF3C), + if (node?.level?.id == chooseLevel?.id) + Positioned( + top: setSize( + context: context, + mobile: -20, + tablet: -30, ), - gradient: const LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Color(0XFF48D336), Color(0XFF2D7C23)], + child: MyImage( + image: MyAssets.location, + size: setSize( + context: context, + mobile: 26, + tablet: 40, + ), ), ), - child: const MyImage(image: MyAssets.doneRounded), - ), - ), - ], + if (type(node?.level?.order ?? levelIndex ?? 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: const EdgeInsets.all(3), + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + width: 1, + color: const Color(0XFF3CFF3C), + ), + gradient: const LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Color(0XFF48D336), Color(0XFF2D7C23)], + ), + ), + child: const MyImage(image: MyAssets.doneRounded), + ), + ), + ], + ), + ), ), - ), - ), - ); - } - }, + ); + } + }, + ), + ), ); } } diff --git a/lib/features/question/presentation/bloc/question_bloc.dart b/lib/features/question/presentation/bloc/question_bloc.dart index 923682a..1210916 100644 --- a/lib/features/question/presentation/bloc/question_bloc.dart +++ b/lib/features/question/presentation/bloc/question_bloc.dart @@ -318,6 +318,7 @@ class QuestionBloc extends Bloc { AnswerEntity(), showConfetti: true, ); + scrollController.jumpTo(0); answerAnimationController?.reverse(); await Future.delayed(const Duration(seconds: 1), () async { final QuestionEntity? findPreQuestion = state.currentQuestion; @@ -359,7 +360,7 @@ class QuestionBloc extends Bloc { playWrongAudio(); await Future.delayed(const Duration(milliseconds: 1500), () { if (globeAnimationController?.value == - globeStates[MyAnimations.globeStateWrong]) { + globeStates[MyAnimations.globeStateWrong]) { changeGlobeState(key: MyAnimations.globeStateAfterWrong); } }); diff --git a/lib/features/question/presentation/ui/screens/question_screen.dart b/lib/features/question/presentation/ui/screens/question_screen.dart index 7b4dd80..21c76a1 100644 --- a/lib/features/question/presentation/ui/screens/question_screen.dart +++ b/lib/features/question/presentation/ui/screens/question_screen.dart @@ -37,6 +37,8 @@ class QuestionScreen extends StatefulWidget { class _QuestionScreenState extends State with TickerProviderStateMixin, WidgetsBindingObserver { late final isTablet = MyDevice.isTablet(context); + late final boxRation = isTablet ? 320 / 300 : 180 / 250; + @override void initState() { super.initState(); @@ -99,6 +101,7 @@ class _QuestionScreenState extends State return FadeAnimController( controller: context.read().imageAnimationController!, child: SingleChildScrollView( + controller: context.read().scrollController, child: Column( children: [ _titles(context), @@ -178,224 +181,61 @@ class _QuestionScreenState extends State builder: (context, state) => Column( spacing: 30, children: [ - Row( - key: Key('${state.currentQuestion?.id}answer0'), - spacing: 20, - children: [ - Expanded( - child: Builder( - key: Key('${state.currentQuestion?.id}0'), - builder: (context) { - if (state.currentQuestion?.answers?[0].imageId == - null) { - return const SizedBox.shrink(); - } else { - return Container( - alignment: isTablet ? Alignment.center : null, - width: isTablet ? 320 : 180, - height: isTablet ? 300 :250, - child: SlideAnim( - controller: context - .read() - .answerAnimationController!, - index: 0, - child: AnswerBox( - index: - state - .currentQuestion - ?.answers?[0] - .order ?? - 1, - answer: - state.currentQuestion?.answers?[0] ?? - AnswerEntity(), - correctAnswer: - state.currentQuestion?.correctAnswer ?? 0, - onNotifTap: (AnswerEntity answer) { - context - .read() - .showAnswerDialog( - context: context, - answerEntity: answer, - ); - }, - onTap: (isCorrect, correctAnswer) => - context.read().add( - ChooseAnswerEvent( - isCorrect, - correctAnswer, - context, - ), - ), - ), - ), - ); - } - }, - ), - ), - Expanded( - child: Builder( - key: Key('${state.currentQuestion?.id}1'), - builder: (context) { - if (state.currentQuestion?.answers?[1].imageId == - null) { - return const SizedBox.shrink(); - } else { - return Container( - alignment: isTablet ? Alignment.center : null, - width: isTablet ? 320 : 180, - height: isTablet ? 300 :250, - child: SlideAnim( - controller: context - .read() - .answerAnimationController!, - index: 1, - child: AnswerBox( - index: - state - .currentQuestion - ?.answers?[1] - .order ?? - 1, - answer: - state.currentQuestion?.answers?[1] ?? - AnswerEntity(), - correctAnswer: - state.currentQuestion?.correctAnswer ?? 0, - onNotifTap: (AnswerEntity answer) { - context - .read() - .showAnswerDialog( - context: context, - answerEntity: answer, - ); - }, - onTap: (isCorrect, correctAnswer) => - context.read().add( - ChooseAnswerEvent( - isCorrect, - correctAnswer, - context, - ), - ), - ), - ), - ); - } - }, - ), - ), - ], - ), - if ((state.currentQuestion?.answers?.length ?? 0) == 3) Row( - key: Key('${state.currentQuestion?.id}answer1'), - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Builder( - key: Key('${state.currentQuestion?.id}2'), - builder: (context) { - if (state.currentQuestion?.answers?[2].imageId == - null) { - return const SizedBox.shrink(); - } else { - return Container( - alignment: isTablet ? Alignment.center : null, - width: isTablet ? 320 : 180, - height: isTablet ? 300 :250, - child: SlideAnim( - controller: context - .read() - .answerAnimationController!, - index: 2, - child: AnswerBox( - index: - state - .currentQuestion - ?.answers?[2] - .order ?? - 1, - answer: - state.currentQuestion?.answers?[2] ?? - AnswerEntity(), - correctAnswer: - state.currentQuestion?.correctAnswer ?? 0, - onNotifTap: (AnswerEntity answer) { - context - .read() - .showAnswerDialog( - context: context, - answerEntity: answer, - ); - }, - onTap: (isCorrect, correctAnswer) => - context.read().add( - ChooseAnswerEvent( - isCorrect, - correctAnswer, - context, - ), - ), - ), - ), - ); - } - }, - ), - ], - ), - if ((state.currentQuestion?.answers?.length ?? 0) == 4) - Row( - key: Key('${state.currentQuestion?.id}answer2'), + key: Key('${state.currentQuestion?.id}answer0'), spacing: 20, children: [ Expanded( child: Builder( - key: Key('${state.currentQuestion?.id}2'), + key: Key('${state.currentQuestion?.id}0'), builder: (context) { - if (state.currentQuestion?.answers?[2].imageId == + if (state.currentQuestion?.answers?[0].imageId == null) { return const SizedBox.shrink(); } else { - return Container( - alignment: isTablet ? Alignment.center : null, - width: isTablet ? 320 : 180, - height: isTablet ? 300 :250, - child: SlideAnim( - controller: context - .read() - .answerAnimationController!, - index: 2, - child: AnswerBox( - index: - state - .currentQuestion - ?.answers?[2] - .order ?? - 1, - answer: - state.currentQuestion?.answers?[2] ?? - AnswerEntity(), - correctAnswer: - state.currentQuestion?.correctAnswer ?? - 0, - onNotifTap: (AnswerEntity answer) { - context - .read() - .showAnswerDialog( - context: context, - answerEntity: answer, - ); - }, - onTap: (isCorrect, correctAnswer) => - context.read().add( - ChooseAnswerEvent( - isCorrect, - correctAnswer, - context, + return AspectRatio( + aspectRatio: boxRation, + child: Container( + alignment: isTablet ? Alignment.center : null, + width: isTablet ? 320 : 180, + height: isTablet ? 300 : 250, + child: SlideAnim( + controller: context + .read() + .answerAnimationController!, + index: 0, + child: AnswerBox( + index: + state + .currentQuestion + ?.answers?[0] + .order ?? + 1, + answer: + state.currentQuestion?.answers?[0] ?? + AnswerEntity(), + correctAnswer: + state + .currentQuestion + ?.correctAnswer ?? + 0, + onNotifTap: (AnswerEntity answer) { + context + .read() + .showAnswerDialog( + context: context, + answerEntity: answer, + ); + }, + onTap: (isCorrect, correctAnswer) => + context.read().add( + ChooseAnswerEvent( + isCorrect, + correctAnswer, + context, + ), ), - ), + ), ), ), ); @@ -405,50 +245,55 @@ class _QuestionScreenState extends State ), Expanded( child: Builder( - key: Key('${state.currentQuestion?.id}3'), + key: Key('${state.currentQuestion?.id}1'), builder: (context) { - if (state.currentQuestion?.answers?[3].imageId == + if (state.currentQuestion?.answers?[1].imageId == null) { return const SizedBox.shrink(); } else { - return Container( - alignment: isTablet ? Alignment.center : null, - width: isTablet ? 320 : 180, - height: isTablet ? 300 :250, - child: SlideAnim( - controller: context - .read() - .answerAnimationController!, - index: 3, - child: AnswerBox( - index: - state - .currentQuestion - ?.answers?[3] - .order ?? - 1, - answer: - state.currentQuestion?.answers?[3] ?? - AnswerEntity(), - correctAnswer: - state.currentQuestion?.correctAnswer ?? - 0, - onNotifTap: (AnswerEntity answer) { - context - .read() - .showAnswerDialog( - context: context, - answerEntity: answer, - ); - }, - onTap: (isCorrect, correctAnswer) => - context.read().add( - ChooseAnswerEvent( - isCorrect, - correctAnswer, - context, + return AspectRatio( + aspectRatio: boxRation, + child: Container( + alignment: isTablet ? Alignment.center : null, + width: isTablet ? 320 : 180, + height: isTablet ? 300 : 250, + child: SlideAnim( + controller: context + .read() + .answerAnimationController!, + index: 1, + child: AnswerBox( + index: + state + .currentQuestion + ?.answers?[1] + .order ?? + 1, + answer: + state.currentQuestion?.answers?[1] ?? + AnswerEntity(), + correctAnswer: + state + .currentQuestion + ?.correctAnswer ?? + 0, + onNotifTap: (AnswerEntity answer) { + context + .read() + .showAnswerDialog( + context: context, + answerEntity: answer, + ); + }, + onTap: (isCorrect, correctAnswer) => + context.read().add( + ChooseAnswerEvent( + isCorrect, + correctAnswer, + context, + ), ), - ), + ), ), ), ); @@ -458,6 +303,200 @@ class _QuestionScreenState extends State ), ], ), + if ((state.currentQuestion?.answers?.length ?? 0) == 3) + Row( + key: Key('${state.currentQuestion?.id}answer1'), + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Builder( + key: Key('${state.currentQuestion?.id}2'), + builder: (context) { + if (state.currentQuestion?.answers?[2].imageId == + null) { + return const SizedBox.shrink(); + } else { + return AspectRatio( + aspectRatio: boxRation, + child: Container( + alignment: isTablet ? Alignment.center : null, + width: isTablet ? 320 : 180, + height: isTablet ? 300 : 250, + child: SlideAnim( + controller: context + .read() + .answerAnimationController!, + index: 2, + child: AnswerBox( + index: + state + .currentQuestion + ?.answers?[2] + .order ?? + 1, + answer: + state.currentQuestion?.answers?[2] ?? + AnswerEntity(), + correctAnswer: + state + .currentQuestion + ?.correctAnswer ?? + 0, + onNotifTap: (AnswerEntity answer) { + context + .read() + .showAnswerDialog( + context: context, + answerEntity: answer, + ); + }, + onTap: (isCorrect, correctAnswer) => + context.read().add( + ChooseAnswerEvent( + isCorrect, + correctAnswer, + context, + ), + ), + ), + ), + ), + ); + } + }, + ), + ], + ), + if ((state.currentQuestion?.answers?.length ?? 0) == 4) + Row( + key: Key('${state.currentQuestion?.id}answer2'), + spacing: 20, + children: [ + Expanded( + child: Builder( + key: Key('${state.currentQuestion?.id}2'), + builder: (context) { + if (state.currentQuestion?.answers?[2].imageId == + null) { + return const SizedBox.shrink(); + } else { + return AspectRatio( + aspectRatio: boxRation, + child: Container( + alignment: isTablet + ? Alignment.center + : null, + width: isTablet ? 320 : 180, + height: isTablet ? 300 : 250, + child: SlideAnim( + controller: context + .read() + .answerAnimationController!, + index: 2, + child: AnswerBox( + index: + state + .currentQuestion + ?.answers?[2] + .order ?? + 1, + answer: + state + .currentQuestion + ?.answers?[2] ?? + AnswerEntity(), + correctAnswer: + state + .currentQuestion + ?.correctAnswer ?? + 0, + onNotifTap: (AnswerEntity answer) { + context + .read() + .showAnswerDialog( + context: context, + answerEntity: answer, + ); + }, + onTap: (isCorrect, correctAnswer) => + context.read().add( + ChooseAnswerEvent( + isCorrect, + correctAnswer, + context, + ), + ), + ), + ), + ), + ); + } + }, + ), + ), + Expanded( + child: Builder( + key: Key('${state.currentQuestion?.id}3'), + builder: (context) { + if (state.currentQuestion?.answers?[3].imageId == + null) { + return const SizedBox.shrink(); + } else { + return AspectRatio( + aspectRatio: boxRation, + child: Container( + alignment: isTablet + ? Alignment.center + : null, + width: isTablet ? 320 : 180, + height: isTablet ? 300 : 250, + child: SlideAnim( + controller: context + .read() + .answerAnimationController!, + index: 3, + child: AnswerBox( + index: + state + .currentQuestion + ?.answers?[3] + .order ?? + 1, + answer: + state + .currentQuestion + ?.answers?[3] ?? + AnswerEntity(), + correctAnswer: + state + .currentQuestion + ?.correctAnswer ?? + 0, + onNotifTap: (AnswerEntity answer) { + context + .read() + .showAnswerDialog( + context: context, + answerEntity: answer, + ); + }, + onTap: (isCorrect, correctAnswer) => + context.read().add( + ChooseAnswerEvent( + isCorrect, + correctAnswer, + context, + ), + ), + ), + ), + ), + ); + } + }, + ), + ), + ], + ), ], ), ), @@ -475,24 +514,21 @@ class _QuestionScreenState extends State alignment: AlignmentDirectional.centerStart, children: [ AnimatedBuilder( - animation: context - .read() - .globeAnimationController!, - builder: (context, child) => - Gif( - image: AssetImage(context - .read() - .statesGlobe[context - .read() - .globeAnimationController - ?.value ?? + animation: context.read().globeAnimationController!, + builder: (context, child) => Gif( + image: AssetImage( + context.read().statesGlobe[context + .read() + .globeAnimationController + ?.value ?? 0] ?? - MyAnimations.globeStateSpeaking), - fps: 10, - autostart: Autostart.loop, - width: 80, - height: 80, - ) + MyAnimations.globeStateSpeaking, + ), + fps: 10, + autostart: Autostart.loop, + width: 80, + height: 80, + ), ), Positioned( left: 90, @@ -520,7 +556,9 @@ class _QuestionScreenState extends State show: false, ); context.read().pausePlaying(); - context.read().changeGlobeState(key: MyAnimations.globeStateNormal); + context.read().changeGlobeState( + key: MyAnimations.globeStateNormal, + ); } }, style: TextButton.styleFrom(