35 changed files with 713 additions and 164 deletions
-
26assets/images/button_3.svg
-
BINassets/images/diamond_big.png
-
BINassets/images/happy_persons.png
-
BINassets/images/shiny.png
-
BINassets/images/ship.png
-
5lib/common_ui/resources/my_assets.dart
-
10lib/common_ui/resources/my_text_style.dart
-
2lib/core/routers/my_routes.dart
-
7lib/core/widgets/answer_box/answer_box.dart
-
62lib/core/widgets/confetti/my_confetti.dart
-
1lib/features/home/presentation/bloc/home_bloc.dart
-
9lib/features/level/presentation/bloc/level_bloc.dart
-
8lib/features/level/presentation/ui/level_page.dart
-
26lib/features/question/data/datasource/question_datasource.dart
-
18lib/features/question/data/repository_impl/question_repository_impl.dart
-
5lib/features/question/domain/entity/answer_entity.dart
-
10lib/features/question/domain/entity/question_entity.dart
-
8lib/features/question/domain/entity/question_entity.g.dart
-
1lib/features/question/domain/repository/question_repository.dart
-
17lib/features/question/domain/usecases/get_next_level_usecase.dart
-
89lib/features/question/presentation/bloc/question_bloc.dart
-
8lib/features/question/presentation/bloc/question_event.dart
-
4lib/features/question/presentation/bloc/question_state.dart
-
137lib/features/question/presentation/ui/question_page.dart
-
164lib/features/question/presentation/ui/screens/diamond_screen.dart
-
141lib/features/question/presentation/ui/screens/question_screen.dart
-
3lib/features/question/presentation/ui/widgets/left_blob.dart
-
6lib/features/question/presentation/ui/widgets/question_stepper.dart
-
3lib/features/question/presentation/ui/widgets/right_blob.dart
-
2lib/init_bindings.dart
-
8lib/l10n/app_en.arb
-
36lib/l10n/app_localizations.dart
-
18lib/l10n/app_localizations_en.dart
-
8pubspec.lock
-
1pubspec.yaml
@ -0,0 +1,26 @@ |
|||
<svg width="198" height="94" viewBox="0 0 198 94" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<g filter="url(#filter0_d_1_4072)"> |
|||
<path d="M188.461 75.7215C188.278 75.8967 188.081 76.0595 187.873 76.2088C171.323 88.5762 27.7109 88.1393 7.66767 77.0322C6.79486 76.6133 6.06848 75.9786 5.5714 75.2006C-1.50808 61.7579 2.33508 21.1271 13.5887 12.3389C25.6882 2.92896 170.11 -1.33912 185.041 10.6586C196.055 19.4131 201.278 63.4214 188.461 75.7215Z" fill="url(#paint0_linear_1_4072)"/> |
|||
</g> |
|||
<path d="M5.62633 72.529C12.4668 85.5516 174.357 86.5934 188.461 73.0667C201.333 60.7665 196.055 16.7415 185.096 7.97009C170.165 -3.99397 25.7431 0.257303 13.662 9.65043C2.38999 18.4218 -1.45315 59.0526 5.62633 72.529Z" fill="url(#paint1_linear_1_4072)"/> |
|||
<defs> |
|||
<filter id="filter0_d_1_4072" x="0.369676" y="3.31946" width="197.233" height="89.8485" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> |
|||
<feFlood flood-opacity="0" result="BackgroundImageFix"/> |
|||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> |
|||
<feOffset dy="6.11371"/> |
|||
<feGaussianBlur stdDeviation="0.815162"/> |
|||
<feComposite in2="hardAlpha" operator="out"/> |
|||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> |
|||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1_4072"/> |
|||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1_4072" result="shape"/> |
|||
</filter> |
|||
<linearGradient id="paint0_linear_1_4072" x1="99.0206" y1="85.4339" x2="99.0206" y2="3.31544" gradientUnits="userSpaceOnUse"> |
|||
<stop stop-color="#8B9AD1"/> |
|||
<stop offset="1" stop-color="#CEE0FF"/> |
|||
</linearGradient> |
|||
<linearGradient id="paint1_linear_1_4072" x1="99.0203" y1="82.7623" x2="99.0203" y2="0.643783" gradientUnits="userSpaceOnUse"> |
|||
<stop stop-color="#CADCFF"/> |
|||
<stop offset="1" stop-color="white"/> |
|||
</linearGradient> |
|||
</defs> |
|||
</svg> |
After Width: 244 | Height: 186 | Size: 39 KiB |
After Width: 150 | Height: 125 | Size: 35 KiB |
After Width: 441 | Height: 759 | Size: 274 KiB |
After Width: 156 | Height: 117 | Size: 15 KiB |
@ -0,0 +1,62 @@ |
|||
import 'dart:math'; |
|||
|
|||
import 'package:confetti/confetti.dart'; |
|||
import 'package:flutter/material.dart'; |
|||
|
|||
class MyConfetti extends StatelessWidget { |
|||
const MyConfetti({super.key, required this.controller}); |
|||
|
|||
final ConfettiController controller; |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return ConfettiWidget( |
|||
confettiController: controller, |
|||
shouldLoop: false, |
|||
blastDirectionality: BlastDirectionality.explosive, |
|||
blastDirection: pi / 2, |
|||
// ↓ down |
|||
gravity: 0.2, |
|||
// let pieces fall |
|||
emissionFrequency: 0.5, |
|||
numberOfParticles: 15, |
|||
particleDrag: 0.1, |
|||
colors: const [ |
|||
Colors.red, |
|||
Colors.orange, |
|||
Colors.yellow, |
|||
Colors.green, |
|||
Colors.blue, |
|||
Colors.purple, |
|||
Colors.pink, |
|||
Colors.cyan, |
|||
], |
|||
createParticlePath: (size) { |
|||
double degToRad(double deg) => deg * (pi / 180.0); |
|||
|
|||
const numberOfPoints = 5; |
|||
final halfWidth = size.width / 2; |
|||
final externalRadius = halfWidth; |
|||
final internalRadius = halfWidth / 2.5; |
|||
final degreesPerStep = degToRad(360 / numberOfPoints); |
|||
final halfDegreesPerStep = degreesPerStep / 2; |
|||
final path = Path(); |
|||
final fullAngle = degToRad(360); |
|||
path.moveTo(size.width, halfWidth); |
|||
|
|||
for (double step = 0; step < fullAngle; step += degreesPerStep) { |
|||
path.lineTo( |
|||
halfWidth + externalRadius * cos(step), |
|||
halfWidth + externalRadius * sin(step), |
|||
); |
|||
path.lineTo( |
|||
halfWidth + internalRadius * cos(step + halfDegreesPerStep), |
|||
halfWidth + internalRadius * sin(step + halfDegreesPerStep), |
|||
); |
|||
} |
|||
path.close(); |
|||
return path; |
|||
}, |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,17 @@ |
|||
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/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/question/domain/repository/question_repository.dart'; |
|||
|
|||
class GetNextLevelUseCase implements UseCase<LevelEntity, QuestionParams> { |
|||
final IQuestionRepository repository; |
|||
|
|||
const GetNextLevelUseCase(this.repository); |
|||
|
|||
@override |
|||
Future<DataState<LevelEntity, MyException>> call(QuestionParams params) { |
|||
return repository.getNextLevel(params: params); |
|||
} |
|||
} |
@ -0,0 +1,164 @@ |
|||
import 'dart:ui'; |
|||
|
|||
import 'package:flutter/material.dart'; |
|||
import 'package:flutter_bloc/flutter_bloc.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; |
|||
import 'package:hadi_hoda_flutter/core/utils/gap.dart'; |
|||
import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; |
|||
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; |
|||
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; |
|||
import 'package:hadi_hoda_flutter/core/widgets/button/enum/button_type.dart'; |
|||
import 'package:hadi_hoda_flutter/core/widgets/button/my_button.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_bloc.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_event.dart'; |
|||
|
|||
class DiamondScreen extends StatelessWidget { |
|||
const DiamondScreen({super.key}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return Column( |
|||
mainAxisAlignment: MainAxisAlignment.spaceAround, |
|||
mainAxisSize: MainAxisSize.max, |
|||
children: [ |
|||
_title(context), |
|||
80.0.gapHeight, |
|||
Column( |
|||
children: [ |
|||
_diamonds(context), |
|||
_mainText(context), |
|||
], |
|||
), |
|||
Spacer(), |
|||
Stack( |
|||
alignment: Alignment.center, |
|||
clipBehavior: Clip.none, |
|||
children: [ |
|||
_ship(context), |
|||
_btns(context), |
|||
], |
|||
), |
|||
], |
|||
); |
|||
} |
|||
|
|||
Text _title(BuildContext context) { |
|||
return Text( |
|||
context.translate.you_win, |
|||
style: Marhey.semiBold22.copyWith(color: MyColors.white), |
|||
); |
|||
} |
|||
|
|||
SizedBox _diamonds(BuildContext context) { |
|||
return SizedBox( |
|||
width: context.widthScreen, |
|||
height: context.heightScreen / 3, |
|||
child: Stack( |
|||
alignment: Alignment.center, |
|||
children: [ |
|||
PositionedDirectional( |
|||
start: 20, |
|||
top: 0, |
|||
child: SizedBox( |
|||
height: 50, |
|||
width: 50, |
|||
child: Transform.rotate( |
|||
angle: -0.5, |
|||
child: Stack( |
|||
children: [ |
|||
MyImage(image: MyAssets.diamondBig), |
|||
ClipRRect( |
|||
child: BackdropFilter( |
|||
filter: ImageFilter.blur(sigmaX: 4, sigmaY: 4), |
|||
child: SizedBox(width: 50, height: 50), |
|||
), |
|||
), |
|||
], |
|||
), |
|||
), |
|||
), |
|||
), |
|||
PositionedDirectional( |
|||
end: 0, |
|||
top: 30, |
|||
child: Transform.rotate( |
|||
angle: 0.5, |
|||
child: Stack( |
|||
children: [ |
|||
MyImage(image: MyAssets.diamondBig, size: 60), |
|||
ClipRRect( |
|||
child: BackdropFilter( |
|||
filter: ImageFilter.blur(sigmaX: 3, sigmaY: 3), |
|||
child: SizedBox(width: 100, height: 100), |
|||
), |
|||
), |
|||
], |
|||
), |
|||
), |
|||
), |
|||
Positioned(top: 100, child: MyImage(image: MyAssets.diamondBig)), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
|
|||
Widget _mainText(BuildContext context){ |
|||
return ShaderMask( |
|||
blendMode: BlendMode.srcIn, |
|||
shaderCallback: (bounds) => LinearGradient( |
|||
begin: Alignment.centerLeft, |
|||
end: Alignment.centerRight, |
|||
colors: [MyColors.white, Color(0XFF63D4F9)], |
|||
).createShader(bounds), |
|||
child: Text( |
|||
context.translate.you_got_diamond, |
|||
style: Marhey |
|||
.bold26, // The color here will be overridden by the ShaderMask |
|||
), |
|||
); |
|||
} |
|||
|
|||
PositionedDirectional _ship(BuildContext context) { |
|||
return PositionedDirectional( |
|||
end: context.widthScreen / 10, |
|||
top: -80, |
|||
child: MyImage(image: MyAssets.ship), |
|||
); |
|||
} |
|||
|
|||
Row _btns(BuildContext context) { |
|||
return Row( |
|||
children: [ |
|||
Expanded( |
|||
child: InkWell( |
|||
onTap: () => context.read<QuestionBloc>().goToLevelPage(context: context), |
|||
child: Stack( |
|||
alignment: Alignment.center, |
|||
children: [ |
|||
MyImage(image: MyAssets.button3, size: 84), |
|||
Positioned( |
|||
top: 10, |
|||
child: Text( |
|||
context.translate.view_map, |
|||
style: DinoKids.regular35.copyWith( |
|||
color: Color(0XFFD93D16), |
|||
), |
|||
), |
|||
), |
|||
], |
|||
), |
|||
), |
|||
), |
|||
Expanded( |
|||
child: MyButton( |
|||
onTap: () => context.read<QuestionBloc>().add(GetNextLevelEvent()), |
|||
title: context.translate.go_next, |
|||
type: ButtonType.type2, |
|||
), |
|||
), |
|||
], |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,141 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:flutter_bloc/flutter_bloc.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; |
|||
import 'package:hadi_hoda_flutter/core/utils/gap.dart'; |
|||
import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; |
|||
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; |
|||
import 'package:hadi_hoda_flutter/core/widgets/answer_box/answer_box.dart'; |
|||
import 'package:hadi_hoda_flutter/core/widgets/showcase/question_showcase.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_bloc.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_event.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_state.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/left_blob.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/refresh_button.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/right_blob.dart'; |
|||
|
|||
class QuestionScreen extends StatelessWidget { |
|||
const QuestionScreen({super.key}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return Column( |
|||
children: [ |
|||
_titles(), |
|||
MySpaces.s14.gapHeight, |
|||
_answers(), |
|||
_bottomDetail(context), |
|||
], |
|||
); |
|||
} |
|||
|
|||
Column _titles() { |
|||
return Column( |
|||
spacing: MySpaces.s4, |
|||
children: [ |
|||
BlocBuilder<QuestionBloc, QuestionState>( |
|||
builder: (context, state) => Text( |
|||
'${context.translate.question} ${state.currentQuestion?.order ?? 1} / ${(state.levelEntity?.questions?.length ?? 0) - 1}', |
|||
style: Marhey.medium12.copyWith( |
|||
color: MyColors.white.withValues(alpha: 0.5), |
|||
shadows: [ |
|||
Shadow( |
|||
offset: Offset(0, 1), |
|||
blurRadius: 1, |
|||
color: Color(0xFF000000).withValues(alpha: 0.25), |
|||
), |
|||
], |
|||
), |
|||
), |
|||
), |
|||
BlocBuilder<QuestionBloc, QuestionState>( |
|||
builder: (context, state) => Text( |
|||
state.currentQuestion?.title ?? '', |
|||
textAlign: TextAlign.center, |
|||
style: Marhey.semiBold22.copyWith( |
|||
color: MyColors.white, |
|||
shadows: [ |
|||
Shadow( |
|||
offset: Offset(0, 1), |
|||
blurRadius: 1, |
|||
color: Color(0xFF000000).withValues(alpha: 0.25), |
|||
), |
|||
], |
|||
), |
|||
), |
|||
), |
|||
], |
|||
); |
|||
} |
|||
|
|||
Expanded _answers() { |
|||
return Expanded( |
|||
child: GridView.builder( |
|||
itemCount: 4, |
|||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( |
|||
crossAxisCount: 2, |
|||
crossAxisSpacing: MySpaces.s20, |
|||
mainAxisSpacing: 50, |
|||
), |
|||
itemBuilder: (context, index) => QuestionShowcase( |
|||
globalKey: context.read<QuestionBloc>().keys[index], |
|||
description: context.translate.tap_to_select, |
|||
child: BlocBuilder<QuestionBloc, QuestionState>( |
|||
builder: (context, state) => AnswerBox( |
|||
key: Key('${state.currentQuestion?.id}'), |
|||
index: state.currentQuestion?.answers?[index].order ?? 1, |
|||
answer: state.currentQuestion?.answers?[index] ?? AnswerEntity(), |
|||
correctAnswer: state.currentQuestion?.correctAnswer ?? 0, |
|||
onTap: (isCorrect) => |
|||
context.read<QuestionBloc>().add( |
|||
ChooseAnswerEvent(isCorrect), |
|||
), |
|||
), |
|||
), |
|||
), |
|||
), |
|||
); |
|||
} |
|||
|
|||
Widget _bottomDetail(BuildContext context) { |
|||
return Row( |
|||
children: [ |
|||
Spacer(), |
|||
BlocBuilder<QuestionBloc, QuestionState>( |
|||
builder: (context, state) => Stack( |
|||
clipBehavior: Clip.none, |
|||
children: [ |
|||
if(state.correctAnswer == false) |
|||
PositionedDirectional( |
|||
start: -100, |
|||
top: -10, |
|||
child: LeftBlob(), |
|||
), |
|||
MyImage( |
|||
image: state.correctAnswer == true |
|||
? MyAssets.happyPersons |
|||
: MyAssets.persons, |
|||
fit: BoxFit.contain, |
|||
size: 110, |
|||
), |
|||
if(state.correctAnswer == false) |
|||
PositionedDirectional( |
|||
top: -30, |
|||
end: -90, |
|||
child: RightBlob(), |
|||
), |
|||
], |
|||
), |
|||
), |
|||
Spacer(), |
|||
RefreshButton( |
|||
onTap: () {}, |
|||
), |
|||
], |
|||
); |
|||
} |
|||
} |
|||
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue