2 Commits
595c1e722c
...
7dd8a7f571
Author | SHA1 | Message | Date |
---|---|---|---|
|
7dd8a7f571 |
Merge pull request 'completed main logic' (#17) from fix/question into develop
Reviewed-on: https://git.nwhco.ir/amirreza.chegini/hade_hoda_flutter/pulls/17 |
15 hours ago |
|
e9d77e20b7 |
completed main logic
|
15 hours ago |
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
-
17lib/features/level/presentation/bloc/level_bloc.dart
-
26lib/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
-
97lib/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