Browse Source
Merge pull request 'feature/question' (#7) from feature/question into develop
Merge pull request 'feature/question' (#7) from feature/question into develop
Reviewed-on: https://git.nwhco.ir/amirreza.chegini/hade_hoda_flutter/pulls/7pull/10/head
40 changed files with 913 additions and 45 deletions
-
10assets/images/bubble_chat_left.svg
-
3assets/images/bubble_chat_right.svg
-
17assets/images/correct.svg
-
BINassets/images/diamond.png
-
4assets/images/done.svg
-
4assets/images/hand_point.svg
-
3assets/images/home.svg
-
3assets/images/music.svg
-
BINassets/images/pattern.png
-
BINassets/images/persons.png
-
17assets/images/wrong.svg
-
11lib/common_ui/resources/my_assets.dart
-
13lib/core/params/question_params.dart
-
11lib/core/routers/my_routes.dart
-
6lib/core/widgets/answer_box/answer_box.dart
-
86lib/core/widgets/answer_box/styles/picture_box.dart
-
62lib/core/widgets/answer_box/styles/text_box.dart
-
50lib/core/widgets/showcase/question_showcase.dart
-
5lib/features/intro/presentation/ui/intro_page.dart
-
28lib/features/question/data/datasource/question_datasource.dart
-
13lib/features/question/data/model/question_model.dart
-
29lib/features/question/data/repository_impl/question_repository_impl.dart
-
14lib/features/question/domain/entity/question_entity.dart
-
8lib/features/question/domain/repository/question_repository.dart
-
17lib/features/question/domain/usecases/get_question_usecase.dart
-
52lib/features/question/presentation/bloc/question_bloc.dart
-
5lib/features/question/presentation/bloc/question_event.dart
-
15lib/features/question/presentation/bloc/question_state.dart
-
178lib/features/question/presentation/ui/question_page.dart
-
41lib/features/question/presentation/ui/widgets/glassy_button.dart
-
27lib/features/question/presentation/ui/widgets/left_blob.dart
-
102lib/features/question/presentation/ui/widgets/question_stepper.dart
-
32lib/features/question/presentation/ui/widgets/refresh_button.dart
-
27lib/features/question/presentation/ui/widgets/right_blob.dart
-
9lib/init_bindings.dart
-
3lib/l10n/app_en.arb
-
6lib/l10n/app_localizations.dart
-
3lib/l10n/app_localizations_en.dart
-
42pubspec.lock
-
2pubspec.yaml
@ -0,0 +1,10 @@ |
|||
<svg width="99" height="74" viewBox="0 0 99 74" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<g clip-path="url(#clip0_6_220)"> |
|||
<path d="M49.4006 68.5179C22.3902 68.5179 0.490861 54.2312 0.490861 36.6075C0.490861 18.9839 22.3902 4.6972 49.4006 4.6972C76.411 4.6972 98.3103 18.9839 98.3103 36.6075C98.3103 44.6307 93.7678 51.9701 86.2663 57.5772C86.8593 62.2087 88.8157 67.287 90.9799 71.098C91.4629 71.9459 90.5275 72.8941 89.3109 72.7483C75.5184 71.0616 73.7515 67.7951 70.1872 66.4457C65.5055 67.3801 54.2471 68.5263 49.4006 68.5179Z" fill="#2F2487"/> |
|||
</g> |
|||
<defs> |
|||
<clipPath id="clip0_6_220"> |
|||
<rect width="97.8194" height="72.957" fill="white" transform="matrix(-1 0 0 1 98.3102 0.213867)"/> |
|||
</clipPath> |
|||
</defs> |
|||
</svg> |
@ -0,0 +1,3 @@ |
|||
<svg width="98" height="69" viewBox="0 0 98 69" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<path d="M48.9351 63.8829C75.9455 63.8829 97.8448 49.5961 97.8448 31.9725C97.8448 14.3489 75.9455 0.0621948 48.9351 0.0621948C21.9247 0.0621948 0.0253601 14.3489 0.0253601 31.9725C0.0253601 39.9957 4.56785 47.3351 12.0694 52.9422C11.4763 57.5737 9.51996 62.652 7.3557 66.463C6.87272 67.3109 7.80812 68.2591 9.02475 68.1132C22.8173 66.4266 24.5841 63.1601 28.1484 61.8107C32.8302 62.745 44.0886 63.8913 48.9351 63.8829Z" fill="#2F2487"/> |
|||
</svg> |
@ -0,0 +1,17 @@ |
|||
<svg width="56" height="51" viewBox="0 0 56 51" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<path d="M51.9315 40.1706C50.273 47.4826 8.41234 52.8752 4.94604 45.3438C1.79486 38.5071 4.18298 10.6119 6.86977 5.71281C10.5517 -0.941101 46.6743 3.73857 49.7259 8.96665C52.4625 13.8474 53.6729 32.6758 51.9315 40.1706Z" fill="url(#paint0_radial_498_2733)"/> |
|||
<path d="M51.8976 38.6412C50.2314 45.5766 8.42476 49.5346 4.95895 42.2723C1.80973 35.7181 4.1427 10.6451 6.84203 5.96088C10.5245 -0.411776 46.8321 4.14531 49.7314 9.08366C52.564 13.7678 53.6472 31.4697 51.8976 38.6412Z" fill="#83FF9A"/> |
|||
<path d="M51.1313 38.0099C49.4846 44.7332 9.07525 48.6218 5.71605 41.6441C2.66971 35.2661 4.95877 10.7532 7.5605 6.28314C11.1173 0.104962 46.1418 4.46602 49.0235 9.31771C51.7076 13.8423 52.8109 31.1049 51.1313 38.0099Z" fill="url(#paint1_linear_498_2733)"/> |
|||
<path d="M42.7617 10.3223L45.8039 13.8678C45.8039 13.8678 39.6579 27.7548 27.4283 35.2666L24.021 31.7743C29.745 23.0196 34.8523 18.2151 42.7617 10.3223Z" fill="white"/> |
|||
<path d="M14.4469 22.8692L18.1136 19.4287C18.1136 19.4287 23.6853 22.6213 30.7432 32.8774L27.3564 35.4214C18.7247 29.3454 19.7432 29.9525 14.4469 22.8692Z" fill="white"/> |
|||
<defs> |
|||
<radialGradient id="paint0_radial_498_2733" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(43.704 56.9891) rotate(180) scale(23.8475 33.2277)"> |
|||
<stop stop-color="#83FF87"/> |
|||
<stop offset="1" stop-color="#33E04D"/> |
|||
</radialGradient> |
|||
<linearGradient id="paint1_linear_498_2733" x1="28.0944" y1="45.3329" x2="28.0944" y2="3.21222" gradientUnits="userSpaceOnUse"> |
|||
<stop stop-color="#21B738"/> |
|||
<stop offset="1" stop-color="#31C747"/> |
|||
</linearGradient> |
|||
</defs> |
|||
</svg> |
After Width: 46 | Height: 38 | Size: 2.6 KiB |
@ -0,0 +1,4 @@ |
|||
<svg width="13" height="11" viewBox="0 0 13 11" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<path d="M11.558 0.348633L12.7359 1.73042C12.7359 1.73042 10.3562 7.14247 5.62103 10.07L4.30176 8.70894C6.51804 5.29707 8.49556 3.42465 11.558 0.348633Z" fill="white"/> |
|||
<path d="M0.594727 5.23829L2.01443 3.89746C2.01443 3.89746 4.17176 5.14167 6.90451 9.1387L5.59316 10.1301C2.25105 7.7622 2.64541 7.99882 0.594727 5.23829Z" fill="white"/> |
|||
</svg> |
@ -0,0 +1,4 @@ |
|||
<svg width="71" height="71" viewBox="0 0 71 71" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<path d="M29.1965 4.74414C25.7116 4.7482 22.3706 6.13436 19.9064 8.59855C17.4422 11.0627 16.0561 14.4037 16.052 17.8886H20.4335C20.4335 15.5645 21.3567 13.3356 23.0001 11.6923C24.6435 10.0489 26.8724 9.12564 29.1965 9.12564C31.5206 9.12564 33.7495 10.0489 35.3929 11.6923C37.0362 13.3356 37.9595 15.5645 37.9595 17.8886H42.341C42.3369 14.4037 40.9507 11.0627 38.4866 8.59855C36.0224 6.13436 32.6814 4.7482 29.1965 4.74414Z" fill="white"/> |
|||
<path d="M46.7231 66.0848H36.9962C34.7982 66.0842 32.6808 65.2577 31.0636 63.7692L10.9088 45.2223C10.4382 44.7921 10.0671 44.2645 9.82125 43.6762C9.57543 43.0879 9.46084 42.4531 9.48545 41.816C9.51005 41.1788 9.67328 40.5548 9.96374 39.9872C10.2542 39.4196 10.6649 38.9222 11.1673 38.5296C12.0279 37.899 13.083 37.5914 14.1477 37.6607C15.2123 37.73 16.2186 38.1718 16.9903 38.9086L24.8156 46.0548V17.8883C24.8156 16.7263 25.2773 15.6118 26.0989 14.7901C26.9206 13.9685 28.0351 13.5068 29.1971 13.5068C30.3592 13.5068 31.4736 13.9685 32.2953 14.7901C33.117 15.6118 33.5786 16.7263 33.5786 17.8883V33.2236C33.5786 32.0615 34.0402 30.9471 34.8619 30.1254C35.6836 29.3037 36.7981 28.8421 37.9601 28.8421C39.1222 28.8421 40.2366 29.3037 41.0583 30.1254C41.88 30.9471 42.3416 32.0615 42.3416 33.2236V35.4143C42.3416 34.2523 42.8032 33.1378 43.6249 32.3161C44.4466 31.4944 45.5611 31.0328 46.7231 31.0328C47.8852 31.0328 48.9996 31.4944 49.8213 32.3161C50.643 33.1378 51.1046 34.2523 51.1046 35.4143V37.6051C51.1046 36.443 51.5662 35.3286 52.3879 34.5069C53.2096 33.6852 54.3241 33.2236 55.4861 33.2236C56.6481 33.2236 57.7626 33.6852 58.5843 34.5069C59.406 35.3286 59.8676 36.443 59.8676 37.6051V52.9403C59.8676 56.4264 58.4827 59.7698 56.0177 62.2348C53.5526 64.6999 50.2092 66.0848 46.7231 66.0848Z" fill="white"/> |
|||
</svg> |
@ -0,0 +1,3 @@ |
|||
<svg width="21" height="22" viewBox="0 0 21 22" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<path d="M20.2727 11.7991L17.6334 12.2735L16.3568 21.4979H13.1477C13.0442 19.4422 12.5783 14.2765 10.4907 14.4698C8.40321 14.6631 8.07547 19.0556 7.9547 21.4979H5.03916C4.20655 18.4616 3.53248 15.3825 3.02035 12.2735H0.708801C0.708801 12.2735 6.83342 0.571633 9.80082 0.308078C12.7682 0.0445223 20.2727 11.7991 20.2727 11.7991Z" fill="#E4E3F7"/> |
|||
</svg> |
@ -0,0 +1,3 @@ |
|||
<svg width="22" height="20" viewBox="0 0 22 20" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<path d="M21.6667 2.04697V14.4808C21.6649 15.4104 21.3367 16.3119 20.7357 17.0376C20.1347 17.7634 19.2966 18.2703 18.3587 18.4755C17.4208 18.6806 16.4386 18.5718 15.573 18.1669C14.7074 17.7619 14.0096 17.0849 13.594 16.2465C13.1784 15.4082 13.0696 14.4583 13.2854 13.5523C13.5012 12.6464 14.0288 11.8381 14.7817 11.2599C15.5347 10.6817 16.4685 10.3677 17.4301 10.3695C18.3918 10.3713 19.3243 10.6887 20.0749 11.2697V7.16617L8.9642 9.11536V15.5785C8.9642 16.3901 8.71525 17.1835 8.24882 17.8583C7.7824 18.5331 7.11945 19.0591 6.34382 19.3697C5.56818 19.6803 4.7147 19.7615 3.89129 19.6032C3.06788 19.4449 2.31153 19.054 1.71788 18.4801C1.12424 17.9062 0.71996 17.1751 0.556174 16.379C0.392388 15.583 0.476449 14.7579 0.797727 14.0081C1.11901 13.2583 1.66307 12.6174 2.36112 12.1665C3.05918 11.7156 3.87986 11.4749 4.7194 11.4749C5.69402 11.4768 6.6383 11.8028 7.39363 12.3982V4.79636C7.38186 4.08709 7.62995 3.39665 8.09402 2.84717C8.56943 2.31042 9.22143 1.94818 9.9405 1.82128L19.3852 0.149074C19.8126 0.0731542 20.254 0.138029 20.6388 0.333319C21.0236 0.528608 21.3295 0.843024 21.5076 1.22626C21.6254 1.48376 21.6805 1.76485 21.6667 2.04697Z" fill="#E4E3F7"/> |
|||
</svg> |
After Width: 412 | Height: 412 | Size: 3.1 KiB |
After Width: 108 | Height: 94 | Size: 24 KiB |
@ -0,0 +1,17 @@ |
|||
<svg width="56" height="51" viewBox="0 0 56 51" fill="none" xmlns="http://www.w3.org/2000/svg"> |
|||
<path d="M51.931 40.1706C50.2725 47.4826 8.41185 52.8752 4.94555 45.3438C1.79438 38.5071 4.18249 10.6118 6.86929 5.71281C10.5512 -0.9411 46.6738 3.73857 49.7254 8.96665C52.462 13.8474 53.6724 32.6758 51.931 40.1706Z" fill="url(#paint0_radial_498_2741)"/> |
|||
<path d="M51.8971 38.6402C50.2309 45.5757 8.42427 49.5336 4.95847 42.2713C1.80925 35.7171 4.14221 10.6441 6.84155 5.9599C10.524 -0.412752 46.8316 4.14433 49.7309 9.08268C52.5635 13.7669 53.6467 31.4687 51.8971 38.6402Z" fill="#FF8385"/> |
|||
<path d="M51.1308 38.0099C49.4841 44.7332 9.07476 48.6218 5.71555 41.6441C2.66922 35.2661 4.95828 10.7532 7.56002 6.28314C11.1168 0.104962 46.1414 4.46602 49.023 9.31771C51.7071 13.8423 52.8104 31.1049 51.1308 38.0099Z" fill="url(#paint1_linear_498_2741)"/> |
|||
<path d="M36.615 10.8369L39.6572 14.3825C39.6572 14.3825 30.2869 29.6865 18.0573 37.1983L15.5018 33.853C21.2258 25.0984 28.3573 17.3243 36.615 10.8369Z" fill="white"/> |
|||
<path d="M15.6688 15.9368L19.2587 12.9521C19.2587 12.9521 35.3009 24.0095 42.3588 34.2656L38.972 36.8096C30.1099 31.1458 22.2395 24.0962 15.6688 15.9368Z" fill="white"/> |
|||
<defs> |
|||
<radialGradient id="paint0_radial_498_2741" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(43.7035 56.9891) rotate(180) scale(23.8475 33.2277)"> |
|||
<stop stop-color="#FF8385"/> |
|||
<stop offset="1" stop-color="#E03336"/> |
|||
</radialGradient> |
|||
<linearGradient id="paint1_linear_498_2741" x1="28.0939" y1="45.3328" x2="28.0939" y2="3.21222" gradientUnits="userSpaceOnUse"> |
|||
<stop stop-color="#C72427"/> |
|||
<stop offset="1" stop-color="#E03336"/> |
|||
</linearGradient> |
|||
</defs> |
|||
</svg> |
@ -0,0 +1,13 @@ |
|||
class QuestionParams { |
|||
int? id; |
|||
|
|||
QuestionParams({this.id}); |
|||
|
|||
QuestionParams copyWith({ |
|||
int? id, |
|||
}) { |
|||
return QuestionParams( |
|||
id: id ?? this.id, |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,50 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:google_fonts/google_fonts.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/theme/my_theme.dart'; |
|||
import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; |
|||
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; |
|||
import 'package:showcaseview/showcaseview.dart'; |
|||
|
|||
class QuestionShowcase extends StatelessWidget { |
|||
const QuestionShowcase({ |
|||
super.key, |
|||
required this.globalKey, |
|||
required this.child, |
|||
this.description, |
|||
}); |
|||
|
|||
final GlobalKey globalKey; |
|||
final String? description; |
|||
final Widget child; |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return Showcase( |
|||
key: globalKey, |
|||
blurValue: 10, |
|||
targetShapeBorder: CircleBorder(), |
|||
tooltipBackgroundColor: Colors.transparent, |
|||
disableMovingAnimation: true, |
|||
textColor: context.primaryColor, |
|||
descriptionTextAlign: TextAlign.center, |
|||
descTextStyle: GoogleFonts.marhey( |
|||
fontSize: 12, |
|||
fontWeight: FontWeight.w700, |
|||
color: context.primaryColor, |
|||
), |
|||
disableScaleAnimation: true, |
|||
tooltipPadding: EdgeInsets.only(top: 60), |
|||
floatingActionWidget: FloatingActionWidget( |
|||
height: 60, |
|||
width: 60, |
|||
right: context.widthScreen * 0.17, |
|||
top: context.widthScreen * 1.17, |
|||
child: MyImage(image: MyAssets.handPoint), |
|||
), |
|||
overlayColor: Color(0XFF0F0041), |
|||
description: description ?? '', |
|||
child: child, |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,28 @@ |
|||
import 'package:hadi_hoda_flutter/core/constants/my_api.dart'; |
|||
import 'package:hadi_hoda_flutter/core/network/http_request.dart'; |
|||
import 'package:hadi_hoda_flutter/core/params/question_params.dart'; |
|||
import 'package:hadi_hoda_flutter/core/response/base_response.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/data/model/question_model.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/domain/entity/question_entity.dart'; |
|||
|
|||
abstract class IQuestionDatasource { |
|||
Future<QuestionEntity> getData({required QuestionParams params}); |
|||
} |
|||
|
|||
class QuestionDatasourceImpl implements IQuestionDatasource { |
|||
final IHttpRequest httpRequest; |
|||
|
|||
const QuestionDatasourceImpl(this.httpRequest); |
|||
|
|||
@override |
|||
Future<QuestionEntity> getData({required QuestionParams params}) async { |
|||
final response = await httpRequest.get( |
|||
path: MyApi.baseUrl, |
|||
); |
|||
|
|||
return BaseResponse.getData<QuestionEntity>( |
|||
response?['data'], |
|||
(json) => QuestionModel.fromJson(json), |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,13 @@ |
|||
import 'package:hadi_hoda_flutter/features/question/domain/entity/question_entity.dart'; |
|||
|
|||
class QuestionModel extends QuestionEntity { |
|||
const QuestionModel({ |
|||
super.id, |
|||
}); |
|||
|
|||
factory QuestionModel.fromJson(Map<String, dynamic> json) { |
|||
return QuestionModel( |
|||
id: json['id'], |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,29 @@ |
|||
import 'package:hadi_hoda_flutter/core/params/question_params.dart'; |
|||
import 'package:flutter/foundation.dart'; |
|||
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; |
|||
import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/data/datasource/question_datasource.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/domain/entity/question_entity.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/domain/repository/question_repository.dart'; |
|||
|
|||
class QuestionRepositoryImpl implements IQuestionRepository { |
|||
final IQuestionDatasource datasource; |
|||
|
|||
const QuestionRepositoryImpl(this.datasource); |
|||
|
|||
@override |
|||
Future<DataState<QuestionEntity, MyException>> getData({required QuestionParams params}) async { |
|||
try { |
|||
final QuestionEntity response = await datasource.getData(params: params); |
|||
return DataState.success(response); |
|||
} on MyException catch (e) { |
|||
return DataState.error(e); |
|||
} catch (e) { |
|||
if (kDebugMode) { |
|||
rethrow; |
|||
} else { |
|||
return DataState.error(MyException(errorMessage: '$e')); |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,14 @@ |
|||
import 'package:equatable/equatable.dart'; |
|||
|
|||
class QuestionEntity extends Equatable { |
|||
final int? id; |
|||
|
|||
const QuestionEntity({ |
|||
this.id, |
|||
}); |
|||
|
|||
@override |
|||
List<Object?> get props => [ |
|||
id, |
|||
]; |
|||
} |
@ -0,0 +1,8 @@ |
|||
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; |
|||
import 'package:hadi_hoda_flutter/core/params/question_params.dart'; |
|||
import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/domain/entity/question_entity.dart'; |
|||
|
|||
abstract class IQuestionRepository { |
|||
Future<DataState<QuestionEntity, MyException>> getData({required QuestionParams params}); |
|||
} |
@ -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/question/domain/entity/question_entity.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/domain/repository/question_repository.dart'; |
|||
|
|||
class GetQuestionUseCase implements UseCase<QuestionEntity, QuestionParams> { |
|||
final IQuestionRepository repository; |
|||
|
|||
const GetQuestionUseCase(this.repository); |
|||
|
|||
@override |
|||
Future<DataState<QuestionEntity, MyException>> call(QuestionParams params) { |
|||
return repository.getData(params: params); |
|||
} |
|||
} |
@ -0,0 +1,52 @@ |
|||
import 'dart:async'; |
|||
import 'package:bloc/bloc.dart'; |
|||
import 'package:flutter/cupertino.dart'; |
|||
import 'package:hadi_hoda_flutter/core/status/base_status.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/domain/entity/question_entity.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/domain/usecases/get_question_usecase.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:showcaseview/showcaseview.dart'; |
|||
|
|||
class QuestionBloc extends Bloc<QuestionEvent, QuestionState> { |
|||
/// ------------constructor------------ |
|||
QuestionBloc( |
|||
this._getQuestionUseCase, |
|||
) : super(const QuestionState()) { |
|||
on<GetQuestionEvent>(_getQuestionEvent); |
|||
} |
|||
|
|||
/// ------------UseCases------------ |
|||
final GetQuestionUseCase _getQuestionUseCase; |
|||
|
|||
/// ------------Variables------------ |
|||
final List<GlobalKey> keys = [ |
|||
GlobalKey(), |
|||
GlobalKey(), |
|||
GlobalKey(), |
|||
GlobalKey(), |
|||
]; |
|||
|
|||
/// ------------Controllers------------ |
|||
|
|||
/// ------------Functions------------ |
|||
void startShowCase({required BuildContext context}) { |
|||
ShowCaseWidget.of(context).startShowCase([keys[1]]); |
|||
} |
|||
|
|||
/// ------------Api Calls------------ |
|||
FutureOr<void> _getQuestionEvent(event, emit) async { |
|||
await _getQuestionUseCase(event.questionParams).then( |
|||
(value) { |
|||
value.fold( |
|||
(data) { |
|||
emit(state.copyWith(getQuestionStatus: BaseComplete<QuestionEntity>(data))); |
|||
}, |
|||
(error) { |
|||
emit(state.copyWith(getQuestionStatus: BaseError(error.errorMessage))); |
|||
}, |
|||
); |
|||
}, |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,5 @@ |
|||
sealed class QuestionEvent { |
|||
const QuestionEvent(); |
|||
} |
|||
|
|||
class GetQuestionEvent extends QuestionEvent {} |
@ -0,0 +1,15 @@ |
|||
import 'package:hadi_hoda_flutter/core/status/base_status.dart'; |
|||
|
|||
class QuestionState { |
|||
final BaseStatus getQuestionStatus; |
|||
|
|||
const QuestionState({this.getQuestionStatus = const BaseInit()}); |
|||
|
|||
QuestionState copyWith({ |
|||
BaseStatus? getQuestionStatus, |
|||
}) { |
|||
return QuestionState( |
|||
getQuestionStatus: getQuestionStatus ?? this.getQuestionStatus, |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,178 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:flutter_bloc/flutter_bloc.dart'; |
|||
import 'package:google_fonts/google_fonts.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.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/answer_box/answer_box.dart'; |
|||
import 'package:hadi_hoda_flutter/core/widgets/showcase/question_showcase.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_bloc.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/glassy_button.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/left_blob.dart'; |
|||
import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/question_stepper.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'; |
|||
import 'package:showcaseview/showcaseview.dart'; |
|||
|
|||
class QuestionPage extends StatelessWidget { |
|||
const QuestionPage({super.key}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return ShowCaseWidget( |
|||
builder: (context) { |
|||
return Scaffold( |
|||
body: Container( |
|||
height: context.heightScreen, |
|||
width: context.widthScreen, |
|||
decoration: BoxDecoration( |
|||
gradient: LinearGradient( |
|||
begin: Alignment.topCenter, |
|||
end: Alignment.bottomCenter, |
|||
colors: [Color(0XFF6930DA), Color(0XFF263AA1)], |
|||
), |
|||
image: DecorationImage( |
|||
image: AssetImage(MyAssets.pattern), |
|||
scale: 3, |
|||
repeat: ImageRepeat.repeat, |
|||
colorFilter: ColorFilter.mode( |
|||
Colors.black.withValues(alpha: 0.3), |
|||
BlendMode.srcIn, |
|||
), |
|||
), |
|||
), |
|||
child: SafeArea( |
|||
child: Padding( |
|||
padding: const EdgeInsets.symmetric(horizontal: MySpaces.s16), |
|||
child: Column( |
|||
children: [ |
|||
MySpaces.s4.gapHeight, |
|||
_topButtons(), |
|||
MySpaces.s10.gapHeight, |
|||
QuestionStepper(), |
|||
_titles(), |
|||
MySpaces.s14.gapHeight, |
|||
_questions(), |
|||
_bottomDetail(context), |
|||
], |
|||
), |
|||
), |
|||
), |
|||
), |
|||
); |
|||
}, |
|||
); |
|||
} |
|||
|
|||
Widget _topButtons() { |
|||
return Row( |
|||
mainAxisAlignment: MainAxisAlignment.spaceBetween, |
|||
crossAxisAlignment: CrossAxisAlignment.center, |
|||
children: [ |
|||
GlassyButton(image: MyAssets.home, onTap: () {}), |
|||
Text( |
|||
'Toothbrushing etiquette', |
|||
style: GoogleFonts.marhey( |
|||
fontSize: 14, |
|||
fontWeight: FontWeight.w700, |
|||
color: Colors.white, |
|||
), |
|||
), |
|||
GlassyButton(image: MyAssets.music, onTap: () {}), |
|||
], |
|||
); |
|||
} |
|||
|
|||
Column _titles() { |
|||
return Column( |
|||
spacing: MySpaces.s4, |
|||
children: [ |
|||
Text( |
|||
'Question 1 / 5', |
|||
style: GoogleFonts.marhey( |
|||
fontSize: 12, |
|||
fontWeight: FontWeight.w500, |
|||
color: Colors.white.withValues(alpha: 0.5), |
|||
shadows: [ |
|||
Shadow( |
|||
offset: Offset(0, 1), |
|||
blurRadius: 1, |
|||
color: Color(0xFF000000).withValues(alpha: 0.25), |
|||
), |
|||
], |
|||
), |
|||
), |
|||
Text( |
|||
'Heda wants her teeth to be clean. Which of her actions do you think is correct?', |
|||
textAlign: TextAlign.center, |
|||
style: GoogleFonts.marhey( |
|||
fontSize: 22, |
|||
fontWeight: FontWeight.w600, |
|||
color: Colors.white, |
|||
shadows: [ |
|||
Shadow( |
|||
offset: Offset(0, 1), |
|||
blurRadius: 1, |
|||
color: Color(0xFF000000).withValues(alpha: 0.25), |
|||
), |
|||
], |
|||
), |
|||
), |
|||
], |
|||
); |
|||
} |
|||
|
|||
Expanded _questions() { |
|||
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: AnswerBox(), |
|||
), |
|||
), |
|||
); |
|||
} |
|||
|
|||
Widget _bottomDetail(BuildContext context) { |
|||
return SizedBox( |
|||
width: context.widthScreen, |
|||
child: Stack( |
|||
clipBehavior: Clip.none, |
|||
alignment: Alignment.center, |
|||
children: [ |
|||
PositionedDirectional( |
|||
start: 0, |
|||
top: -10, |
|||
child: LeftBlob(), |
|||
), |
|||
Padding( |
|||
padding: const EdgeInsetsDirectional.only(end: 60), |
|||
child: MyImage(image: MyAssets.persons), |
|||
), |
|||
PositionedDirectional( |
|||
start: 210, |
|||
top: -20, |
|||
child: RightBlob(), |
|||
), |
|||
PositionedDirectional( |
|||
end: 0, |
|||
bottom: 10, |
|||
child: RefreshButton( |
|||
onTap: () {}, |
|||
), |
|||
), |
|||
], |
|||
), |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,41 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; |
|||
import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; |
|||
|
|||
class GlassyButton extends StatelessWidget { |
|||
const GlassyButton({super.key, required this.image, this.onTap}); |
|||
|
|||
final String image; |
|||
final VoidCallback? onTap; |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return Material( |
|||
color: Colors.transparent, |
|||
child: Ink( |
|||
width: 48, |
|||
height: 48, |
|||
decoration: BoxDecoration( |
|||
shape: BoxShape.circle, |
|||
gradient: LinearGradient( |
|||
begin: Alignment.topCenter, |
|||
end: Alignment.bottomCenter, |
|||
colors: [ |
|||
Colors.white.withValues(alpha: 0.3), |
|||
Color(0XFF6930DA).withValues(alpha: 0.1), |
|||
], |
|||
), |
|||
border: Border.all(color: Colors.white.withValues(alpha: 0.3)), |
|||
), |
|||
child: InkWell( |
|||
onTap: onTap, |
|||
borderRadius: BorderRadius.all(Radius.circular(100)), |
|||
child: Padding( |
|||
padding: const EdgeInsets.all(MySpaces.s12), |
|||
child: MyImage(image: image), |
|||
), |
|||
), |
|||
), |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,27 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:google_fonts/google_fonts.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
|||
import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; |
|||
|
|||
class LeftBlob extends StatelessWidget { |
|||
const LeftBlob({super.key}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return Stack( |
|||
alignment: Alignment.center, |
|||
children: [ |
|||
MyImage(image: MyAssets.bubbleChatLeft), |
|||
Text( |
|||
'Your answer\nwas not correct.', |
|||
textAlign: TextAlign.center, |
|||
style: GoogleFonts.marhey( |
|||
fontSize: 12, |
|||
fontWeight: FontWeight.w500, |
|||
color: Color(0XFFB5AEEE), |
|||
), |
|||
), |
|||
], |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,102 @@ |
|||
import 'package:easy_stepper/easy_stepper.dart'; |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
|||
import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; |
|||
|
|||
class QuestionStepper extends StatelessWidget { |
|||
const QuestionStepper({super.key}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return SizedBox( |
|||
height: 80, |
|||
child: EasyStepper( |
|||
activeStep: 1, |
|||
lineStyle: LineStyle( |
|||
lineLength: 20, |
|||
lineType: LineType.normal, |
|||
defaultLineColor: Color(0XFFDFDDF6), |
|||
lineThickness: 5, |
|||
finishedLineColor: Color(0XFF21B738), |
|||
), |
|||
activeStepBackgroundColor: Colors.transparent, |
|||
finishedStepBackgroundColor: Colors.transparent, |
|||
unreachedStepBackgroundColor: Colors.transparent, |
|||
internalPadding: 0, |
|||
showLoadingAnimation: false, |
|||
stepRadius: 18, |
|||
showStepBorder: false, |
|||
padding: EdgeInsets.all(0), |
|||
enableStepTapping: false, |
|||
steps: List.generate( |
|||
6, |
|||
(index) => EasyStep( |
|||
customStep: index == 5 |
|||
? MyImage(image: MyAssets.diamond, size: 50) |
|||
: ClipPath( |
|||
clipper: _StepperClipper(), |
|||
child: Container( |
|||
height: 32, |
|||
width: 32, |
|||
padding: EdgeInsets.all(4), |
|||
decoration: BoxDecoration( |
|||
color: Color(0XFFDFDDF6), |
|||
shape: BoxShape.circle, |
|||
), |
|||
child: ClipPath( |
|||
clipper: _StepperClipper(), |
|||
child: Container( |
|||
padding: EdgeInsets.all(6), |
|||
decoration: BoxDecoration( |
|||
shape: BoxShape.circle, |
|||
color: index < 1 |
|||
? Color(0XFF21B738) |
|||
: index == 1 |
|||
? Color(0XFF847AC4) |
|||
: Colors.transparent, |
|||
), |
|||
child: index < 1 ? MyImage(image: MyAssets.done) : null, |
|||
), |
|||
), |
|||
), |
|||
), |
|||
), |
|||
), |
|||
), |
|||
); |
|||
} |
|||
} |
|||
|
|||
class _StepperClipper extends CustomClipper<Path> { |
|||
@override |
|||
Path getClip(Size size) { |
|||
// Original SVG viewBox: width=34, height=33 |
|||
final sx = size.width / 34.0; |
|||
final sy = size.height / 33.0; |
|||
|
|||
final p = Path() |
|||
..moveTo(33.3479 * sx, 14.8127 * sy) |
|||
..cubicTo( |
|||
33.3479 * sx, 23.7042 * sy, |
|||
27.2015 * sx, 32.9501 * sy, |
|||
17.8599 * sx, 32.9501 * sy, |
|||
)..cubicTo( |
|||
8.51818 * sx, 32.9501 * sy, |
|||
0.945251 * sx, 25.7421 * sy, |
|||
0.945251 * sx, 16.8507 * sy, |
|||
)..cubicTo( |
|||
0.945251 * sx, 7.95917 * sy, |
|||
8.51818 * sx, 0.751205 * sy, |
|||
17.8599 * sx, 0.751205 * sy, |
|||
)..cubicTo( |
|||
27.2015 * sx, 0.751205 * sy, |
|||
33.3479 * sx, 5.92127 * sy, |
|||
33.3479 * sx, 14.8127 * sy, |
|||
)..close(); |
|||
|
|||
return p; |
|||
} |
|||
|
|||
@override |
|||
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false; |
|||
} |
@ -0,0 +1,32 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/theme/my_theme.dart'; |
|||
|
|||
class RefreshButton extends StatelessWidget { |
|||
const RefreshButton({super.key, this.onTap,}); |
|||
|
|||
final VoidCallback? onTap; |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return Material( |
|||
color: context.noColor, |
|||
child: Ink( |
|||
height: 48, |
|||
width: 48, |
|||
decoration: BoxDecoration( |
|||
shape: BoxShape.circle, |
|||
gradient: LinearGradient( |
|||
begin: Alignment.topCenter, |
|||
end: Alignment.bottomCenter, |
|||
colors: [Color(0XFFA393FF), Color(0XFFC6BCFB)], |
|||
), |
|||
), |
|||
child: InkWell( |
|||
onTap: onTap, |
|||
borderRadius: BorderRadius.all(Radius.circular(100)), |
|||
child: Icon(Icons.refresh, size: 40, color: Color(0XFF263AA1)), |
|||
), |
|||
), |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,27 @@ |
|||
import 'package:flutter/material.dart'; |
|||
import 'package:google_fonts/google_fonts.dart'; |
|||
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; |
|||
import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; |
|||
|
|||
class RightBlob extends StatelessWidget { |
|||
const RightBlob({super.key}); |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return Stack( |
|||
alignment: Alignment.center, |
|||
children: [ |
|||
MyImage(image: MyAssets.bubbleChatRight), |
|||
Text( |
|||
'Be more\ncareful.', |
|||
textAlign: TextAlign.center, |
|||
style: GoogleFonts.marhey( |
|||
fontSize: 12, |
|||
fontWeight: FontWeight.w500, |
|||
color: Color(0XFFB5AEEE), |
|||
), |
|||
), |
|||
], |
|||
); |
|||
} |
|||
} |
@ -1,4 +1,5 @@ |
|||
{ |
|||
"about_us": "About us", |
|||
"about_us_desc" : "Rive combines an interactive design tool, a new stateful graphics format, a lightweight multi-platform runtime, and a blazing-fast vector renderer. \nThis end-to-end pipeline brings interfaces to life with motion. It gives designers and devs the tools to build." |
|||
"about_us_desc" : "Rive combines an interactive design tool, a new stateful graphics format, a lightweight multi-platform runtime, and a blazing-fast vector renderer. \nThis end-to-end pipeline brings interfaces to life with motion. It gives designers and devs the tools to build.", |
|||
"tap_to_select": "Tap the correct option to select." |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue