Browse Source

update

develop
sina 2 days ago
parent
commit
cd555aa141
  1. BIN
      assets/videos/intro_1.mp4
  2. BIN
      assets/videos/intro_1_ar.mp4
  3. BIN
      assets/videos/intro_1_en.mp4
  4. BIN
      assets/videos/intro_2.mp4
  5. BIN
      assets/videos/intro_2_ar.mp4
  6. BIN
      assets/videos/intro_2_en.mp4
  7. BIN
      assets/videos/intro_3.mp4
  8. BIN
      assets/videos/intro_3_ar.mp4
  9. BIN
      assets/videos/intro_3_en.mp4
  10. BIN
      assets/videos/intro_4.mp4
  11. BIN
      assets/videos/intro_4_ar.mp4
  12. BIN
      assets/videos/intro_4_en.mp4
  13. BIN
      assets/videos/intro_5.mp4
  14. BIN
      assets/videos/intro_5_ar.mp4
  15. BIN
      assets/videos/intro_5_en.mp4
  16. 46
      lib/core/widgets/answer_box/answer_box.dart
  17. 3
      lib/core/widgets/answer_box/answer_box_show.dart
  18. 24
      lib/features/intro/presentation/bloc/intro_bloc.dart
  19. 59
      lib/features/level/presentation/bloc/level_bloc.dart
  20. 254
      lib/features/level/presentation/ui/level_page.dart
  21. 249
      lib/features/level/presentation/ui/widgets/node_widget.dart
  22. 3
      lib/features/question/presentation/bloc/question_bloc.dart
  23. 568
      lib/features/question/presentation/ui/screens/question_screen.dart

BIN
assets/videos/intro_1.mp4

BIN
assets/videos/intro_1_ar.mp4

BIN
assets/videos/intro_1_en.mp4

BIN
assets/videos/intro_2.mp4

BIN
assets/videos/intro_2_ar.mp4

BIN
assets/videos/intro_2_en.mp4

BIN
assets/videos/intro_3.mp4

BIN
assets/videos/intro_3_ar.mp4

BIN
assets/videos/intro_3_en.mp4

BIN
assets/videos/intro_4.mp4

BIN
assets/videos/intro_4_ar.mp4

BIN
assets/videos/intro_4_en.mp4

BIN
assets/videos/intro_5.mp4

BIN
assets/videos/intro_5_ar.mp4

BIN
assets/videos/intro_5_en.mp4

46
lib/core/widgets/answer_box/answer_box.dart

@ -35,7 +35,7 @@ class _AnswerBoxState extends State<AnswerBox> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Hero(
return Hero(
tag: 'Hero_answer_${widget.answer.id}', tag: 'Hero_answer_${widget.answer.id}',
child: Material( child: Material(
type: MaterialType.transparency, type: MaterialType.transparency,
@ -68,29 +68,33 @@ class _AnswerBoxState extends State<AnswerBox> {
left: 0, left: 0,
right: 0, right: 0,
bottom: 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),
),
), ),
), ),
),
], ],
), ),
), ),

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

@ -46,7 +46,8 @@ class AnswerBoxShow extends StatelessWidget {
padding: const EdgeInsetsGeometry.all(14), padding: const EdgeInsetsGeometry.all(14),
), ),
), ),
Positioned(
if (answer.audioInfo?.filename?.isNotEmpty == true)
Positioned(
top: setSize(context: context, mobile: MySpaces.s30, tablet: 60), top: setSize(context: context, mobile: MySpaces.s30, tablet: 60),
right: setSize( right: setSize(
context: context, context: context,

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

@ -45,22 +45,24 @@ class IntroBloc extends Bloc<IntroEvent, IntroState> {
Intro4Screen(key: Key('3')), Intro4Screen(key: Key('3')),
Intro5Screen(key: Key('4')), 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------------ /// ------------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------------ /// ------------Functions------------

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

@ -117,71 +117,78 @@ class LevelBloc extends Bloc<LevelEvent, LevelState> {
left: setSize(context: MyContext.get, mobile: 0.15.w, tablet: 0.58.w), left: setSize(context: MyContext.get, mobile: 0.15.w, tablet: 0.58.w),
index: 11, index: 11,
), ),
//////
LevelLocation( LevelLocation(
bottom: setSize(context: MyContext.get, mobile: 0.78.h, tablet: 1.52.h), 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), left: setSize(context: MyContext.get, mobile: 0.4.w, tablet: 0.45.w),
index: 12, 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( 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, index: 14,
), ),
LevelLocation( 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, index: 15,
), ),
LevelLocation( 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, index: 16,
), ),
LevelLocation( 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, index: 17,
), ),
LevelLocation( 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, index: 18,
), ),
LevelLocation( 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, index: 19,
), ),
LevelLocation( 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, index: 20,
), ),
LevelLocation( 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, index: 21,
), ),
LevelLocation( 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, index: 22,
), ),
LevelLocation( 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), left: setSize(context: MyContext.get, mobile: 0.1.w, tablet: 0.1.w),
index: 23, index: 23,
), ),
LevelLocation( 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, 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<NodeEntity> nodeList = []; final List<NodeEntity> nodeList = [];

254
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/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@ -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/node_widget.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/play_button.dart'; import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/play_button.dart';
import '../../domain/entity/level_location.dart';
class LevelPage extends StatefulWidget { class LevelPage extends StatefulWidget {
const LevelPage({super.key});
const LevelPage({super.key});
@override
State<LevelPage> createState() => _LevelPageState();
@override
State<LevelPage> createState() => _LevelPageState();
} }
class _LevelPageState extends State<LevelPage> { class _LevelPageState extends State<LevelPage> {
@ -40,18 +43,24 @@ class _LevelPageState extends State<LevelPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
if(!kDebugMode)_triggerRemainingLevelsDownload();
if (!kDebugMode) _triggerRemainingLevelsDownload();
} }
void _triggerRemainingLevelsDownload() { void _triggerRemainingLevelsDownload() {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted) return; if (!mounted) return;
final maxLevelCount = int.tryParse(LocalStorage.readData(key: MyConstants.maxLevelCount) ?? '20') ?? 20;
context.read<DownloadBloc>().add(StartDownloadEvent(toLevel: maxLevelCount));
final maxLevelCount =
int.tryParse(
LocalStorage.readData(key: MyConstants.maxLevelCount) ?? '20',
) ??
20;
context.read<DownloadBloc>().add(
StartDownloadEvent(toLevel: maxLevelCount),
);
}); });
} }
@override
@override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: MyPopScope( body: MyPopScope(
@ -68,7 +77,6 @@ class _LevelPageState extends State<LevelPage> {
_background(context), _background(context),
_planets(context), _planets(context),
_path(context), _path(context),
], ],
), ),
), ),
@ -78,12 +86,9 @@ class _LevelPageState extends State<LevelPage> {
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
clipBehavior: Clip.none, clipBehavior: Clip.none,
children: [
_ship(context),
_playButton(context),
],
children: [_ship(context), _playButton(context)],
), ),
)
),
], ],
), ),
), ),
@ -214,94 +219,126 @@ class _LevelPageState extends State<LevelPage> {
} }
Widget _path(BuildContext context) { 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<LevelBloc, LevelState>(
builder: (context, state) {
final locationList = context.read<LevelBloc>().locationList;
final nodeList = context.read<LevelBloc>().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<LevelBloc, LevelState>(
builder: (context, state) => Stack(
clipBehavior: Clip.none,
children: [
...List.generate(
context.read<LevelBloc>().nodeList.length,
(index) {
final node = context.read<LevelBloc>().nodeList[index];
if(node.nodeType == NodeType.comingSoon) {
if(node.nodeType == NodeType.comingSoon) {
return Positioned(
left: 0,
right: 0,
bottom: (context.read<LevelBloc>().locationList[index].bottom ?? 0) + 88,
child: const ComingSoonLevel());
}
}
return Positioned(
top: context.read<LevelBloc>().locationList[index].top,
bottom: context.read<LevelBloc>().locationList[index].bottom,
right: context.read<LevelBloc>().locationList[index].right,
left: context.read<LevelBloc>().locationList[index].left,
child: BlocBuilder<LevelBloc, LevelState>(
buildWhen: (previous, current) =>
previous.chooseLevel?.id != current.chooseLevel?.id,
builder: (context, state) => NodeWidget(
chooseLevel: state.chooseLevel,
node: context.read<LevelBloc>().nodeList[index],
type: context.read<LevelBloc>().getLevelType,
getReward: context.read<LevelBloc>().getReward,
onRewardPressed: (prize) {
context.read<LevelBloc>().showReward(
context: context,
prize: prize,
);
},
onTap: (LevelEntity level, LevelType type) {
context.read<LevelBloc>().add(
ChooseLevelEvent(level, type),
);
},
),
),
);
},
),
],
),
);
}
Widget _levelLocation({
required List<LevelLocation> locationList,
required List<NodeEntity> nodeList,
}) {
return BlocBuilder<LevelBloc, LevelState>(
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<LevelBloc, LevelState>(
buildWhen: (previous, current) =>
previous.chooseLevel?.id != current.chooseLevel?.id,
builder: (context, state) => NodeWidget(
chooseLevel: state.chooseLevel,
levelIndex: location.index,
node: node,
type: context.read<LevelBloc>().getLevelType,
getReward: context.read<LevelBloc>().getReward,
onRewardPressed: (prize) {
context.read<LevelBloc>().showReward(
context: context,
prize: prize,
);
},
onTap: (LevelEntity level, LevelType type) {
context.read<LevelBloc>().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<LevelBloc, LevelState>(
buildWhen: (previous, current) =>
previous.chooseLevel?.id != current.chooseLevel?.id,
builder: (context, state) {
return PlayButton(
level: state.chooseLevel ?? LevelEntity(),
onTap: (level) =>
context.read<LevelBloc>().goToQuestionPage(context, level),
);
}
);
}
Widget _playButton(BuildContext context) {
return BlocBuilder<LevelBloc, LevelState>(
buildWhen: (previous, current) =>
previous.chooseLevel?.id != current.chooseLevel?.id,
builder: (context, state) {
return PlayButton(
level: state.chooseLevel ?? LevelEntity(),
onTap: (level) =>
context.read<LevelBloc>().goToQuestionPage(context, level),
);
},
);
}
Positioned _topSection(BuildContext context) { Positioned _topSection(BuildContext context) {
return Positioned( return Positioned(
@ -322,9 +359,7 @@ class _LevelPageState extends State<LevelPage> {
), ),
), ),
const Spacer(), const Spacer(),
DiamondLevel(
diamonds: context.read<LevelBloc>().diamonds,
),
DiamondLevel(diamonds: context.read<LevelBloc>().diamonds),
StreamBuilder<double>( StreamBuilder<double>(
initialData: 1, initialData: 1,
stream: context.read<LevelBloc>().volumeStream, stream: context.read<LevelBloc>().volumeStream,
@ -364,9 +399,26 @@ class _LevelPageState extends State<LevelPage> {
// } // }
Widget _background(BuildContext context) { 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,
),
),
); );
} }
} }

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

@ -42,150 +42,157 @@ class NodeWidget extends StatelessWidget {
required this.getReward, required this.getReward,
required this.type, required this.type,
required this.chooseLevel, required this.chooseLevel,
required this.levelIndex,
this.onTap, this.onTap,
this.onRewardPressed, this.onRewardPressed,
}); });
final LevelType Function(int index) type; final LevelType Function(int index) type;
final bool Function(int index) getReward; final bool Function(int index) getReward;
final NodeEntity node;
final NodeEntity? node;
final int? levelIndex;
final LevelEntity? chooseLevel; final LevelEntity? chooseLevel;
final Function(LevelEntity level, LevelType type)? onTap; final Function(LevelEntity level, LevelType type)? onTap;
final void Function(PrizeEntity prize)? onRewardPressed; final void Function(PrizeEntity prize)? onRewardPressed;
@override @override
Widget build(BuildContext context) { 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( child: Stack(
alignment: Alignment.topCenter,
clipBehavior: Clip.none,
alignment: Alignment.center,
children: [ 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, 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),
),
),
],
),
),
), ),
),
),
);
}
},
);
}
},
),
),
); );
} }
} }

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

@ -318,6 +318,7 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
AnswerEntity(), AnswerEntity(),
showConfetti: true, showConfetti: true,
); );
scrollController.jumpTo(0);
answerAnimationController?.reverse(); answerAnimationController?.reverse();
await Future.delayed(const Duration(seconds: 1), () async { await Future.delayed(const Duration(seconds: 1), () async {
final QuestionEntity? findPreQuestion = state.currentQuestion; final QuestionEntity? findPreQuestion = state.currentQuestion;
@ -359,7 +360,7 @@ class QuestionBloc extends Bloc<QuestionEvent, QuestionState> {
playWrongAudio(); playWrongAudio();
await Future.delayed(const Duration(milliseconds: 1500), () { await Future.delayed(const Duration(milliseconds: 1500), () {
if (globeAnimationController?.value == if (globeAnimationController?.value ==
globeStates[MyAnimations.globeStateWrong]) {
globeStates[MyAnimations.globeStateWrong]) {
changeGlobeState(key: MyAnimations.globeStateAfterWrong); changeGlobeState(key: MyAnimations.globeStateAfterWrong);
} }
}); });

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

@ -37,6 +37,8 @@ class QuestionScreen extends StatefulWidget {
class _QuestionScreenState extends State<QuestionScreen> class _QuestionScreenState extends State<QuestionScreen>
with TickerProviderStateMixin, WidgetsBindingObserver { with TickerProviderStateMixin, WidgetsBindingObserver {
late final isTablet = MyDevice.isTablet(context); late final isTablet = MyDevice.isTablet(context);
late final boxRation = isTablet ? 320 / 300 : 180 / 250;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -99,6 +101,7 @@ class _QuestionScreenState extends State<QuestionScreen>
return FadeAnimController( return FadeAnimController(
controller: context.read<QuestionBloc>().imageAnimationController!, controller: context.read<QuestionBloc>().imageAnimationController!,
child: SingleChildScrollView( child: SingleChildScrollView(
controller: context.read<QuestionBloc>().scrollController,
child: Column( child: Column(
children: [ children: [
_titles(context), _titles(context),
@ -178,224 +181,61 @@ class _QuestionScreenState extends State<QuestionScreen>
builder: (context, state) => Column( builder: (context, state) => Column(
spacing: 30, spacing: 30,
children: [ 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<QuestionBloc>()
.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<QuestionBloc>()
.showAnswerDialog(
context: context,
answerEntity: answer,
);
},
onTap: (isCorrect, correctAnswer) =>
context.read<QuestionBloc>().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<QuestionBloc>()
.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<QuestionBloc>()
.showAnswerDialog(
context: context,
answerEntity: answer,
);
},
onTap: (isCorrect, correctAnswer) =>
context.read<QuestionBloc>().add(
ChooseAnswerEvent(
isCorrect,
correctAnswer,
context,
),
),
),
),
);
}
},
),
),
],
),
if ((state.currentQuestion?.answers?.length ?? 0) == 3)
Row( 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<QuestionBloc>()
.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<QuestionBloc>()
.showAnswerDialog(
context: context,
answerEntity: answer,
);
},
onTap: (isCorrect, correctAnswer) =>
context.read<QuestionBloc>().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, spacing: 20,
children: [ children: [
Expanded( Expanded(
child: Builder( child: Builder(
key: Key('${state.currentQuestion?.id}2'),
key: Key('${state.currentQuestion?.id}0'),
builder: (context) { builder: (context) {
if (state.currentQuestion?.answers?[2].imageId ==
if (state.currentQuestion?.answers?[0].imageId ==
null) { null) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} else { } else {
return Container(
alignment: isTablet ? Alignment.center : null,
width: isTablet ? 320 : 180,
height: isTablet ? 300 :250,
child: SlideAnim(
controller: context
.read<QuestionBloc>()
.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<QuestionBloc>()
.showAnswerDialog(
context: context,
answerEntity: answer,
);
},
onTap: (isCorrect, correctAnswer) =>
context.read<QuestionBloc>().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<QuestionBloc>()
.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<QuestionBloc>()
.showAnswerDialog(
context: context,
answerEntity: answer,
);
},
onTap: (isCorrect, correctAnswer) =>
context.read<QuestionBloc>().add(
ChooseAnswerEvent(
isCorrect,
correctAnswer,
context,
),
), ),
),
),
), ),
), ),
); );
@ -405,50 +245,55 @@ class _QuestionScreenState extends State<QuestionScreen>
), ),
Expanded( Expanded(
child: Builder( child: Builder(
key: Key('${state.currentQuestion?.id}3'),
key: Key('${state.currentQuestion?.id}1'),
builder: (context) { builder: (context) {
if (state.currentQuestion?.answers?[3].imageId ==
if (state.currentQuestion?.answers?[1].imageId ==
null) { null) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} else { } else {
return Container(
alignment: isTablet ? Alignment.center : null,
width: isTablet ? 320 : 180,
height: isTablet ? 300 :250,
child: SlideAnim(
controller: context
.read<QuestionBloc>()
.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<QuestionBloc>()
.showAnswerDialog(
context: context,
answerEntity: answer,
);
},
onTap: (isCorrect, correctAnswer) =>
context.read<QuestionBloc>().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<QuestionBloc>()
.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<QuestionBloc>()
.showAnswerDialog(
context: context,
answerEntity: answer,
);
},
onTap: (isCorrect, correctAnswer) =>
context.read<QuestionBloc>().add(
ChooseAnswerEvent(
isCorrect,
correctAnswer,
context,
),
), ),
),
),
), ),
), ),
); );
@ -458,6 +303,200 @@ class _QuestionScreenState extends State<QuestionScreen>
), ),
], ],
), ),
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<QuestionBloc>()
.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<QuestionBloc>()
.showAnswerDialog(
context: context,
answerEntity: answer,
);
},
onTap: (isCorrect, correctAnswer) =>
context.read<QuestionBloc>().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<QuestionBloc>()
.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<QuestionBloc>()
.showAnswerDialog(
context: context,
answerEntity: answer,
);
},
onTap: (isCorrect, correctAnswer) =>
context.read<QuestionBloc>().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<QuestionBloc>()
.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<QuestionBloc>()
.showAnswerDialog(
context: context,
answerEntity: answer,
);
},
onTap: (isCorrect, correctAnswer) =>
context.read<QuestionBloc>().add(
ChooseAnswerEvent(
isCorrect,
correctAnswer,
context,
),
),
),
),
),
);
}
},
),
),
],
),
], ],
), ),
), ),
@ -475,24 +514,21 @@ class _QuestionScreenState extends State<QuestionScreen>
alignment: AlignmentDirectional.centerStart, alignment: AlignmentDirectional.centerStart,
children: [ children: [
AnimatedBuilder( AnimatedBuilder(
animation: context
.read<QuestionBloc>()
.globeAnimationController!,
builder: (context, child) =>
Gif(
image: AssetImage(context
.read<QuestionBloc>()
.statesGlobe[context
.read<QuestionBloc>()
.globeAnimationController
?.value ??
animation: context.read<QuestionBloc>().globeAnimationController!,
builder: (context, child) => Gif(
image: AssetImage(
context.read<QuestionBloc>().statesGlobe[context
.read<QuestionBloc>()
.globeAnimationController
?.value ??
0] ?? 0] ??
MyAnimations.globeStateSpeaking),
fps: 10,
autostart: Autostart.loop,
width: 80,
height: 80,
)
MyAnimations.globeStateSpeaking,
),
fps: 10,
autostart: Autostart.loop,
width: 80,
height: 80,
),
), ),
Positioned( Positioned(
left: 90, left: 90,
@ -520,7 +556,9 @@ class _QuestionScreenState extends State<QuestionScreen>
show: false, show: false,
); );
context.read<QuestionBloc>().pausePlaying(); context.read<QuestionBloc>().pausePlaying();
context.read<QuestionBloc>().changeGlobeState(key: MyAnimations.globeStateNormal);
context.read<QuestionBloc>().changeGlobeState(
key: MyAnimations.globeStateNormal,
);
} }
}, },
style: TextButton.styleFrom( style: TextButton.styleFrom(

Loading…
Cancel
Save