diff --git a/assets/images/leaf.png b/assets/images/leaf.png new file mode 100644 index 0000000000000000000000000000000000000000..d78212fa7526245294ca5c3d05fe4e8c0e481edf GIT binary patch literal 436 zcmV;l0ZaagP)yvNzy=Nz022u5qFjBcgJDplP`Mh6} zE4UGe*idp3jV$*RpRfi@NO7bHXyYB$V1{%=tP9Cl=?g_i;b^IaEx=@~B2|QCh;zg{ zif>$pQ0Ej=3UKLwrTDEkApW7yI^zrmc~@|3w(ALpVmHeEP9yd>k07Vx=l6JBZ_pe&-F8Q4np{IC&;f~gZiTC-w{*rvIs;UPI^yRliiNn0dyxzhL zQswuOpIHSCgW!z!8YsPU@G_NA{vR{mzM6$I$fLZx7dxgFbRuBc4XLOed)mRRNDAp; eYlun@|A0Td&aU(uKEtvA0000 GoRouter( name: Routes.levelPage, path: Routes.levelPage, builder: (context, state) => BlocProvider( - create: (context) => LevelBloc(locator())..add(GetLevelListEvent()), + create: (context) => LevelBloc(locator())..add(SetCurrentLevelEvent()), child: const LevelPage(), ), ), diff --git a/lib/core/widgets/answer_box/answer_box.dart b/lib/core/widgets/answer_box/answer_box.dart index 7038487..1f36cd1 100644 --- a/lib/core/widgets/answer_box/answer_box.dart +++ b/lib/core/widgets/answer_box/answer_box.dart @@ -1,38 +1,60 @@ import 'package:flutter/material.dart'; -import 'package:hadi_hoda_flutter/core/utils/storage_path.dart'; +import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/core/widgets/answer_box/styles/picture_box.dart'; import 'package:hadi_hoda_flutter/core/widgets/answer_box/styles/text_box.dart'; import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart'; -class AnswerBox extends StatelessWidget { - const AnswerBox({super.key, required this.answer,this.selected, this.onTap, required this.index}); +class AnswerBox extends StatefulWidget { + const AnswerBox({ + super.key, + required this.answer, + required this.correctAnswer, + required this.index, + this.onTap, + }); final AnswerEntity answer; - final bool? selected; - final Function(AnswerEntity answer)? onTap; + final int correctAnswer; + final VoidCallback? onTap; final int index; + @override + State createState() => _AnswerBoxState(); +} + +class _AnswerBoxState extends State { + + bool selected = false; + + @override Widget build(BuildContext context) { return GestureDetector( - onTap: () => onTap?.call(answer), + onTap: !selected ? () { + setState(() { + selected = true; + }); + if (selected && (widget.index == widget.correctAnswer)) { + widget.onTap?.call(); + } + } : null, child: SizedBox( child: Stack( alignment: Alignment.bottomCenter, clipBehavior: Clip.none, children: [ AnswerPictureBox( - selected: selected ?? false, - index: index, - image: '${StoragePath.documentDir.path}/data/${answer - .imageId}${answer.imageInfo?.extension ?? '.png'}', + selected: selected, + index: widget.index, + image: widget.answer.image ?? '', + correctAnswer: widget.correctAnswer, ), Positioned( left: 0, right: 0, - bottom: -36, + bottom: -MySpaces.s26, child: AnswerTextBox( - text: answer.title ?? '', + text: widget.answer.title ?? '', ), ), ], diff --git a/lib/core/widgets/answer_box/styles/picture_box.dart b/lib/core/widgets/answer_box/styles/picture_box.dart index 6609a71..64550c0 100644 --- a/lib/core/widgets/answer_box/styles/picture_box.dart +++ b/lib/core/widgets/answer_box/styles/picture_box.dart @@ -1,31 +1,92 @@ import 'dart:io'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.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/my_image.dart'; +import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/black_white_effect.dart'; -class AnswerPictureBox extends StatelessWidget { - const AnswerPictureBox({super.key, required this.selected, required this.image, required this.index}); +class AnswerPictureBox extends StatefulWidget { + const AnswerPictureBox({ + super.key, + required this.selected, + required this.image, + required this.index, + required this.correctAnswer, + }); final bool selected; final String image; final int index; + final int correctAnswer; + + @override + State createState() => _AnswerPictureBoxState(); +} + +class _AnswerPictureBoxState extends State { + late Future _imageFuture; + + @override + void initState() { + super.initState(); + _imageFuture = File(widget.image).readAsBytes(); + } @override Widget build(BuildContext context) { return CustomPaint( size: Size(170, 170), - foregroundPainter: _SvgCustomPainter(selected), + foregroundPainter: _SvgCustomPainter(false), child: ClipPath( clipper: _SvgCustomClipper(), child: Stack( children: [ - Image.file( - File(image), - fit: BoxFit.cover, - height: 170, - width: 170, + FutureBuilder( + future: _imageFuture, + builder: (context, snapshot) { + return AnimatedCrossFade( + crossFadeState: snapshot.hasData + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, + duration: Duration(milliseconds: 300), + firstChild: SizedBox( + height: 170, + width: 170, + child: Center( + child: CupertinoActivityIndicator( + color: MyColors.white, + ), + ), + ), + secondChild: Builder( + builder: (context) { + if (widget.selected && + (widget.index != widget.correctAnswer)) { + return BlackWhiteEffect( + child: Image.memory( + snapshot.data ?? Uint8List(0), + fit: BoxFit.cover, + height: 170, + width: 170, + ), + ); + } else { + return Image.memory( + snapshot.data ?? Uint8List(0), + fit: BoxFit.cover, + height: 170, + width: 170, + ); + } + }, + ) + ); + } ), PositionedDirectional( top: MySpaces.s12, @@ -47,7 +108,7 @@ class AnswerPictureBox extends StatelessWidget { ), ), child: Text( - '$index', + '${widget.index}', style: Marhey.semiBold17.copyWith( color: MyColors.white, ), @@ -55,14 +116,16 @@ class AnswerPictureBox extends StatelessWidget { ), ), ), - // PositionedDirectional( - // top: MySpaces.s14, - // end: MySpaces.s12, - // child: MyImage( - // image: MyAssets.correct, - // size: MySpaces.s40, - // ), - // ), + if(widget.selected) + PositionedDirectional( + top: MySpaces.s14, + end: MySpaces.s12, + child: MyImage( + image: widget.index == widget.correctAnswer ? MyAssets.correct : MyAssets + .wrong, + size: MySpaces.s40, + ), + ), ], ), ), diff --git a/lib/core/widgets/hadith_dialog/hadith_dialog.dart b/lib/core/widgets/hadith_dialog/hadith_dialog.dart new file mode 100644 index 0000000..ff5d098 --- /dev/null +++ b/lib/core/widgets/hadith_dialog/hadith_dialog.dart @@ -0,0 +1,80 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.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/check_platform.dart'; +import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; +import 'package:hadi_hoda_flutter/core/widgets/about_us_dialog/styles/background.dart'; + +Future showHadithDialog({required BuildContext context}) async { + await showDialog( + context: context, + builder: (context) => HadithDialog(), + barrierColor: MyColors.purple.withValues(alpha: 0.82), + useSafeArea: false, + ); +} + +class HadithDialog extends StatelessWidget { + const HadithDialog({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: MyColors.transparent, + body: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 6, sigmaY: 6), + child: Center( + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: checkSize( + context: context, + mobile: 18, + tablet: 120, + ) ?? 0, + ), + child: Stack( + clipBehavior: Clip.none, + children: [ + AboutUSDialogBackground( + child: ListView.separated( + itemCount: 3, + separatorBuilder: (context, index) => Divider( + height: 40, + thickness: 1, + endIndent: MySpaces.s20, + indent: MySpaces.s20, + color: Color(0xFFC2BDE4), + ), + itemBuilder: (context, index) => Text( + 'Prophet Muhammad (PBUH) said: "Teeth, which are smooth and beautiful, become dirty as a result of chewing food, and gradually the smell of the mouth changes and causes corruption in the nasal organs. When a person brushes her teeth, the corruption disappears and the teeth become clean and pure again."', + style: Marhey.medium14.copyWith( + color: Color(0XFF494178), + ), + ), + ), + ), + Positioned( + right: checkSize(context: context, mobile: 30, tablet: 40), + top: -12, + child: GestureDetector( + onTap: context.pop, + behavior: HitTestBehavior.opaque, + child: MyImage( + image: MyAssets.closeBtn, + size: checkSize(context: context, mobile: 40, tablet: 60), + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/features/level/data/datasource/level_datasource.dart b/lib/features/level/data/datasource/level_datasource.dart index e3c75fe..a1b6bea 100644 --- a/lib/features/level/data/datasource/level_datasource.dart +++ b/lib/features/level/data/datasource/level_datasource.dart @@ -20,8 +20,8 @@ class LocalLevelDatasourceImpl implements ILevelDatasource { final String selectedLanguage = LocalStorage.readData( key: MyConstants.selectLanguage); final Box levelBox = Hive.box(MyConstants.levelBox); - final TotalDataEntity findData = levelBox.values.singleWhere((e) => - e.code == selectedLanguage, + final TotalDataEntity findData = levelBox.values.singleWhere( + (e) => e.code == selectedLanguage, orElse: () => TotalDataEntity(), ); return findData.levels ?? []; diff --git a/lib/features/level/presentation/bloc/level_bloc.dart b/lib/features/level/presentation/bloc/level_bloc.dart index 2cb4c92..29c071b 100644 --- a/lib/features/level/presentation/bloc/level_bloc.dart +++ b/lib/features/level/presentation/bloc/level_bloc.dart @@ -20,6 +20,7 @@ class LevelBloc extends Bloc { this._getLeveslUseCase, ) : super(const LevelState()) { on(_getLevelListEvent); + on(_setCurrentLevelEvent); on(_startScrollEvent); } @@ -81,10 +82,9 @@ class LevelBloc extends Bloc { } LevelType getLevelType(int index) { - final int currentLevel = int.parse(LocalStorage - .readData(key: MyConstants.currentLevel) - .isEmpty ? '1' : LocalStorage - .readData(key: MyConstants.currentLevel)); + final int currentLevel = int.parse( + LocalStorage.readData(key: MyConstants.currentLevel), + ); if (index < currentLevel) { return LevelType.finished; @@ -98,6 +98,9 @@ class LevelBloc extends Bloc { /// ------------Api Calls------------ FutureOr _getLevelListEvent(GetLevelListEvent event, Emitter emit) async { + final int currentLevel = int.parse( + LocalStorage.readData(key: MyConstants.currentLevel), + ); await _getLeveslUseCase(LevelParams()).then((value) { value.fold( (data) async { @@ -107,7 +110,7 @@ class LevelBloc extends Bloc { } emit(state.copyWith( getLevelStatus: const BaseComplete(''), - chooseLevel: data.first, + chooseLevel: data.singleWhere((e) => e.order == currentLevel), )); add(StartScrollEvent()); }, @@ -120,10 +123,9 @@ class LevelBloc extends Bloc { StartScrollEvent event, Emitter emit, ) async { - final int currentLevel = int.parse(LocalStorage - .readData(key: MyConstants.currentLevel) - .isEmpty ? '1' : LocalStorage - .readData(key: MyConstants.currentLevel)); + final int currentLevel = int.parse( + LocalStorage.readData(key: MyConstants.currentLevel), + ); await Future.delayed(const Duration(seconds: 1)); if (scrollController.hasClients) { @@ -142,4 +144,14 @@ class LevelBloc extends Bloc { } } } + + FutureOr _setCurrentLevelEvent(SetCurrentLevelEvent event, + Emitter emit) async { + final String currentLevel = LocalStorage.readData( + key: MyConstants.currentLevel); + if (currentLevel.isEmpty) { + await LocalStorage.saveData(key: MyConstants.currentLevel, value: '1'); + } + add(GetLevelListEvent()); + } } diff --git a/lib/features/level/presentation/bloc/level_event.dart b/lib/features/level/presentation/bloc/level_event.dart index d081e54..88f0cca 100644 --- a/lib/features/level/presentation/bloc/level_event.dart +++ b/lib/features/level/presentation/bloc/level_event.dart @@ -6,6 +6,7 @@ sealed class LevelEvent { class GetLevelListEvent extends LevelEvent {} class StartScrollEvent extends LevelEvent {} +class SetCurrentLevelEvent extends LevelEvent {} class ChooseLevelEvent extends LevelEvent { final LevelEntity level; const ChooseLevelEvent(this.level); diff --git a/lib/features/level/presentation/ui/level_page.dart b/lib/features/level/presentation/ui/level_page.dart index d971070..08ba410 100644 --- a/lib/features/level/presentation/ui/level_page.dart +++ b/lib/features/level/presentation/ui/level_page.dart @@ -43,22 +43,16 @@ class LevelPage extends StatelessWidget { buildWhen: (previous, current) => previous.chooseLevel?.id != current.chooseLevel?.id, builder: (context, state) { - if (state.chooseLevel != null) { - return Positioned( - bottom: MediaQuery - .viewPaddingOf(context) - .bottom + MySpaces.s10, - right: MySpaces.s16, - left: MySpaces.s16, - child: HintLevelWidget( - level: state.chooseLevel ?? LevelEntity(), - onTap: (level) => - context.read().goToQuestionPage(context, level), - ), - ); - } else { - return SizedBox.shrink(); - } + return Positioned( + bottom: MediaQuery.viewPaddingOf(context).bottom + MySpaces.s10, + right: MySpaces.s16, + left: MySpaces.s16, + child: HintLevelWidget( + level: state.chooseLevel ?? LevelEntity(), + onTap: (level) => + context.read().goToQuestionPage(context, level), + ), + ); } ); } @@ -108,7 +102,6 @@ class LevelPage extends StatelessWidget { right: context.read().topLocationList[index].right, left: context.read().topLocationList[index].left, child: LevelWidget( - index: context.read().topLocationList[index].index ?? 0, level: context.read().top12LevelList[index], type: context.read().getLevelType(index + 9), onTap: (LevelEntity level) {}, @@ -141,7 +134,6 @@ class LevelPage extends StatelessWidget { right: context.read().bottomLocationList[index].right, left: context.read().bottomLocationList[index].left, child: LevelWidget( - index: context.read().bottomLocationList[index].index ?? 0, level: context.read().bottom8LevelList[index], type: context.read().getLevelType(index + 1), onTap: (LevelEntity level) {}, diff --git a/lib/features/level/presentation/ui/widgets/hint_level_widget.dart b/lib/features/level/presentation/ui/widgets/hint_level_widget.dart index ad98952..c5a95bd 100644 --- a/lib/features/level/presentation/ui/widgets/hint_level_widget.dart +++ b/lib/features/level/presentation/ui/widgets/hint_level_widget.dart @@ -3,12 +3,17 @@ 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/common_ui/resources/my_text_style.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/styles/text_box.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; class HintLevelWidget extends StatelessWidget { - const HintLevelWidget({super.key, required this.level, this.onTap,}); + const HintLevelWidget({ + super.key, + required this.level, + this.onTap, + }); final LevelEntity level; final Function(LevelEntity level)? onTap; @@ -36,7 +41,7 @@ class HintLevelWidget extends StatelessWidget { spacing: MySpaces.s8, children: [ Text( - 'Step ${level.order ?? 0}', + '${context.translate.step} ${level.order ?? 0}', style: Marhey.bold14.copyWith( color: Color(0xFFD8490B), ), diff --git a/lib/features/level/presentation/ui/widgets/level_widget.dart b/lib/features/level/presentation/ui/widgets/level_widget.dart index 420c6c4..fea8e5a 100644 --- a/lib/features/level/presentation/ui/widgets/level_widget.dart +++ b/lib/features/level/presentation/ui/widgets/level_widget.dart @@ -20,13 +20,11 @@ enum LevelType { class LevelWidget extends StatelessWidget { const LevelWidget({ super.key, - required this.index, required this.level, this.type, this.onTap, }); - final int index; final LevelType? type; final LevelEntity level; final Function(LevelEntity level)? onTap; @@ -41,7 +39,7 @@ class LevelWidget extends StatelessWidget { children: [ MyImage(image: LevelType.image[type] ?? MyAssets.level, size: 46), Text( - '$index', + '${level.order}', style: DinoKids.regular26.copyWith( color: MyColors.white, shadows: [ diff --git a/lib/features/question/data/datasource/question_datasource.dart b/lib/features/question/data/datasource/question_datasource.dart index cd05fe3..f83a2b7 100644 --- a/lib/features/question/data/datasource/question_datasource.dart +++ b/lib/features/question/data/datasource/question_datasource.dart @@ -1,28 +1,34 @@ import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/error_handler/my_exception.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/utils/local_storage.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; +import 'package:hadi_hoda_flutter/features/level/domain/entity/total_data_entity.dart'; import 'package:hive/hive.dart'; abstract class IQuestionDatasource { Future getLevel({required QuestionParams params}); } +/// Local class QuestionDatasourceImpl implements IQuestionDatasource { - final IHttpRequest httpRequest; - - const QuestionDatasourceImpl(this.httpRequest); + const QuestionDatasourceImpl(); @override Future getLevel({required QuestionParams params}) async { try { - final Box levelBox = Hive.box(MyConstants.levelBox); - final LevelEntity findLevel = levelBox.values.singleWhere( + final String selectedLanguage = LocalStorage.readData( + key: MyConstants.selectLanguage); + final Box levelBox = Hive.box(MyConstants.levelBox); + final TotalDataEntity findData = levelBox.values.singleWhere( + (e) => e.code == selectedLanguage, + orElse: () => TotalDataEntity(), + ); + final LevelEntity? findLevel = findData.levels?.singleWhere( (e) => e.id == params.id, orElse: () => LevelEntity(), ); - return findLevel; + return findLevel ?? LevelEntity(); } catch (e) { throw MyException(errorMessage: '$e'); } diff --git a/lib/features/question/domain/entity/answer_entity.dart b/lib/features/question/domain/entity/answer_entity.dart index d2fa120..b883bbd 100644 --- a/lib/features/question/domain/entity/answer_entity.dart +++ b/lib/features/question/domain/entity/answer_entity.dart @@ -1,3 +1,6 @@ +import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; +import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; +import 'package:hadi_hoda_flutter/core/utils/storage_path.dart'; import 'package:hadi_hoda_flutter/features/question/domain/entity/file_entity.dart'; import 'package:hive/hive.dart'; @@ -17,6 +20,8 @@ class AnswerEntity extends HiveObject { int? order; @HiveField(5) bool? isActive; + @HiveField(6) + String? image; AnswerEntity({ this.id, @@ -25,5 +30,9 @@ class AnswerEntity extends HiveObject { this.imageInfo, this.order, this.isActive, - }); + this.image, + }){ + image = + '${StoragePath.documentDir.path}/${LocalStorage.readData(key: MyConstants.selectLanguage)}/files/images/${imageInfo?.filename ?? ''}'; + } } diff --git a/lib/features/question/domain/entity/answer_entity.g.dart b/lib/features/question/domain/entity/answer_entity.g.dart index 17f432c..e8a7616 100644 --- a/lib/features/question/domain/entity/answer_entity.g.dart +++ b/lib/features/question/domain/entity/answer_entity.g.dart @@ -23,13 +23,14 @@ class AnswerEntityAdapter extends TypeAdapter { imageInfo: fields[3] as FileEntity?, order: fields[4] as int?, isActive: fields[5] as bool?, + image: fields[6] as String?, ); } @override void write(BinaryWriter writer, AnswerEntity obj) { writer - ..writeByte(6) + ..writeByte(7) ..writeByte(0) ..write(obj.id) ..writeByte(1) @@ -41,7 +42,9 @@ class AnswerEntityAdapter extends TypeAdapter { ..writeByte(4) ..write(obj.order) ..writeByte(5) - ..write(obj.isActive); + ..write(obj.isActive) + ..writeByte(6) + ..write(obj.image); } @override diff --git a/lib/features/question/presentation/bloc/question_bloc.dart b/lib/features/question/presentation/bloc/question_bloc.dart index 5c660ea..e270d2e 100644 --- a/lib/features/question/presentation/bloc/question_bloc.dart +++ b/lib/features/question/presentation/bloc/question_bloc.dart @@ -3,6 +3,8 @@ import 'package:bloc/bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:hadi_hoda_flutter/core/params/question_params.dart'; import 'package:hadi_hoda_flutter/core/status/base_status.dart'; +import 'package:hadi_hoda_flutter/core/widgets/hadith_dialog/hadith_dialog.dart'; +import 'package:hadi_hoda_flutter/features/question/domain/entity/question_entity.dart'; import 'package:hadi_hoda_flutter/features/question/domain/usecases/get_level_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'; @@ -14,7 +16,7 @@ class QuestionBloc extends Bloc { this._getLevelUseCase, ) : super(const QuestionState()) { on(_getLevelEvent); - on(_chooseAnswerEvent); + on(_changeQuestionEvent); } /// ------------UseCases------------ @@ -36,11 +38,11 @@ class QuestionBloc extends Bloc { ShowCaseWidget.of(context).startShowCase([keys[1]]); } - FutureOr _chooseAnswerEvent(ChooseAnswerEvent event, Emitter emit) { - emit(state.copyWith(chooseAnswer: event.answer)); + void showHadith({required BuildContext context}) { + showHadithDialog(context: context); } - /// ------------Api Calls------------ + /// ------------Event Calls------------ FutureOr _getLevelEvent(GetLevelEvent event, Emitter emit) async { await _getLevelUseCase(QuestionParams(id: int.parse(event.id ?? '0'))).then( (value) { @@ -49,6 +51,7 @@ class QuestionBloc extends Bloc { emit(state.copyWith( getQuestionStatus: BaseComplete(''), levelEntity: data, + currentQuestion: data.questions?.first, )); }, (error) { @@ -58,4 +61,18 @@ class QuestionBloc extends Bloc { }, ); } + + FutureOr _changeQuestionEvent( + ChangeQuestionEvent event, + Emitter emit, + ) async { + await Future.delayed(Duration(seconds: 1), () { + final QuestionEntity? findPreQuestion = state.currentQuestion; + final int findPreIndex = (findPreQuestion?.order ?? 1) - 1; + final int newIndex = findPreIndex + 1; + emit( + state.copyWith(currentQuestion: state.levelEntity?.questions?[newIndex]), + ); + }); + } } diff --git a/lib/features/question/presentation/bloc/question_event.dart b/lib/features/question/presentation/bloc/question_event.dart index fae2b60..cf7aa05 100644 --- a/lib/features/question/presentation/bloc/question_event.dart +++ b/lib/features/question/presentation/bloc/question_event.dart @@ -1,17 +1,11 @@ -import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart'; - sealed class QuestionEvent { const QuestionEvent(); } class GetLevelEvent extends QuestionEvent { final String? id; - const GetLevelEvent(this.id); } -class ChooseAnswerEvent extends QuestionEvent { - final AnswerEntity? answer; - - const ChooseAnswerEvent(this.answer); +class ChangeQuestionEvent extends QuestionEvent { } diff --git a/lib/features/question/presentation/bloc/question_state.dart b/lib/features/question/presentation/bloc/question_state.dart index 8cd1943..3a1d8b6 100644 --- a/lib/features/question/presentation/bloc/question_state.dart +++ b/lib/features/question/presentation/bloc/question_state.dart @@ -1,31 +1,27 @@ import 'package:hadi_hoda_flutter/core/status/base_status.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; -import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart'; +import 'package:hadi_hoda_flutter/features/question/domain/entity/question_entity.dart'; class QuestionState { final BaseStatus getQuestionStatus; final LevelEntity? levelEntity; - final int currentStep; - final AnswerEntity? chooseAnswer; + final QuestionEntity? currentQuestion; const QuestionState({ this.getQuestionStatus = const BaseInit(), this.levelEntity, - this.currentStep = 0, - this.chooseAnswer, + this.currentQuestion, }); QuestionState copyWith({ BaseStatus? getQuestionStatus, LevelEntity? levelEntity, - int? currentStep, - AnswerEntity? chooseAnswer, + QuestionEntity? currentQuestion, }) { return QuestionState( getQuestionStatus: getQuestionStatus ?? this.getQuestionStatus, levelEntity: levelEntity ?? this.levelEntity, - currentStep: currentStep ?? this.currentStep, - chooseAnswer: chooseAnswer ?? this.chooseAnswer, + currentQuestion: currentQuestion ?? this.currentQuestion, ); } } diff --git a/lib/features/question/presentation/ui/question_page.dart b/lib/features/question/presentation/ui/question_page.dart index 9663bf0..f0037a8 100644 --- a/lib/features/question/presentation/ui/question_page.dart +++ b/lib/features/question/presentation/ui/question_page.dart @@ -54,7 +54,7 @@ class QuestionPage extends StatelessWidget { child: Column( children: [ MySpaces.s4.gapHeight, - _topButtons(), + _topButtons(context), MySpaces.s10.gapHeight, _stepper(), _titles(), @@ -72,20 +72,27 @@ class QuestionPage extends StatelessWidget { } - Widget _topButtons() { + Widget _topButtons(BuildContext context) { return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ GlassyButton(image: MyAssets.home, onTap: () {}), + Spacer(), BlocBuilder( builder: (context, state) => Text( - state.levelEntity?.title ?? '', + '${context.translate.step} ${state.levelEntity?.order ?? 1}', style: Marhey.bold14.copyWith( color: MyColors.white, ), ), ), + Spacer(), + GlassyButton( + image: MyAssets.leaf, + onTap: () => context.read().showHadith(context: context), + ), + MySpaces.s10.gapWidth, GlassyButton(image: MyAssets.music, onTap: () {}), ], ); @@ -95,7 +102,7 @@ class QuestionPage extends StatelessWidget { return BlocBuilder( builder: (context, state) => QuestionStepper( length: state.levelEntity?.questions?.length ?? 0, - currentStep: state.currentStep, + currentStep: state.currentQuestion?.order ?? 1, ), ); } @@ -107,7 +114,7 @@ class QuestionPage extends StatelessWidget { children: [ BlocBuilder( builder: (context, state) => Text( - 'Question ${state.currentStep} / ${state.levelEntity?.questions?.length ?? 0}', + '${context.translate.question} ${state.currentQuestion?.order ?? 1} / ${state.levelEntity?.questions?.length ?? 0}', style: Marhey.medium12.copyWith( color: MyColors.white.withValues(alpha: 0.5), shadows: [ @@ -122,9 +129,9 @@ class QuestionPage extends StatelessWidget { ), BlocBuilder( builder: (context, state) => Text( - state.levelEntity?.questions?[state.currentStep].title ?? '', + state.currentQuestion?.title ?? '', textAlign: TextAlign.center, - style: Marhey.medium12.copyWith( + style: Marhey.semiBold22.copyWith( color: MyColors.white, shadows: [ Shadow( @@ -154,12 +161,11 @@ class QuestionPage extends StatelessWidget { description: context.translate.tap_to_select, child: BlocBuilder( builder: (context, state) => AnswerBox( - index: index + 1, - answer: state.levelEntity?.questions?[state.currentStep] - .answers?[index] ?? AnswerEntity(), - selected: state.levelEntity?.questions?[state.currentStep] - .answers?[index].id == state.chooseAnswer?.id, - onTap: (answer) => context.read().add(ChooseAnswerEvent(answer)), + key: Key('${state.currentQuestion?.id}'), + index: state.currentQuestion?.answers?[index].order ?? 1, + answer: state.currentQuestion?.answers?[index] ?? AnswerEntity(), + correctAnswer: state.currentQuestion?.correctAnswer ?? 0, + onTap: () => context.read().add(ChangeQuestionEvent()), ), ), ), diff --git a/lib/features/question/presentation/ui/widgets/black_white_effect.dart b/lib/features/question/presentation/ui/widgets/black_white_effect.dart new file mode 100644 index 0000000..317ab60 --- /dev/null +++ b/lib/features/question/presentation/ui/widgets/black_white_effect.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class BlackWhiteEffect extends StatelessWidget { + const BlackWhiteEffect({super.key, required this.child}); + + final Widget child; + + @override + Widget build(BuildContext context) { + return ColorFiltered( + colorFilter: ColorFilter.matrix( + [ + 0.2126, 0.7152, 0.0722, 0, 0, + 0.2126, 0.7152, 0.0722, 0, 0, + 0.2126, 0.7152, 0.0722, 0, 0, + 0, 0, 0, 1, 0, + ], + ), + child: child, + ); + } +} diff --git a/lib/features/question/presentation/ui/widgets/question_stepper.dart b/lib/features/question/presentation/ui/widgets/question_stepper.dart index dc7a384..555861d 100644 --- a/lib/features/question/presentation/ui/widgets/question_stepper.dart +++ b/lib/features/question/presentation/ui/widgets/question_stepper.dart @@ -12,9 +12,9 @@ class QuestionStepper extends StatelessWidget { @override Widget build(BuildContext context) { return SizedBox( - height: 80, + height: 70, child: EasyStepper( - activeStep: currentStep, + activeStep: currentStep - 1, lineStyle: LineStyle( lineLength: 20, lineType: LineType.normal, @@ -32,9 +32,9 @@ class QuestionStepper extends StatelessWidget { padding: EdgeInsets.all(0), enableStepTapping: false, steps: List.generate( - length, + length + 1, (index) => EasyStep( - customStep: index == length - 1 + customStep: index == length ? MyImage(image: MyAssets.diamond, size: 50) : ClipPath( clipper: _StepperClipper(), @@ -52,13 +52,13 @@ class QuestionStepper extends StatelessWidget { padding: EdgeInsets.all(6), decoration: BoxDecoration( shape: BoxShape.circle, - color: index < currentStep + color: index < currentStep - 1 ? Color(0XFF21B738) - : index == currentStep + 1 + : index == currentStep - 1 ? Color(0XFF847AC4) : Colors.transparent, ), - child: index < currentStep ? MyImage(image: MyAssets.done) : null, + child: index < currentStep - 1 ? MyImage(image: MyAssets.done) : null, ), ), ), diff --git a/lib/init_bindings.dart b/lib/init_bindings.dart index be6cedf..444b3d7 100644 --- a/lib/init_bindings.dart +++ b/lib/init_bindings.dart @@ -53,7 +53,7 @@ void initBindings() { /// Home Feature /// Question Feature - locator.registerLazySingleton(() => QuestionDatasourceImpl(locator())); + locator.registerLazySingleton(() => QuestionDatasourceImpl()); locator.registerLazySingleton(() => QuestionRepositoryImpl(locator())); locator.registerLazySingleton(() => GetLevelUseCase(locator())); diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index efe6555..c2e8258 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -9,5 +9,7 @@ "lost_connection": "Lost connection!", "try_again": "Try Again", "connected_to_internet": "You must be connected to the internet to download the initial game data.", - "start": "Start" + "start": "Start", + "step": "Step", + "question": "Question" } \ No newline at end of file diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 8edc5d4..a2a4745 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -159,6 +159,18 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Start'** String get start; + + /// No description provided for @step. + /// + /// In en, this message translates to: + /// **'Step'** + String get step; + + /// No description provided for @question. + /// + /// In en, this message translates to: + /// **'Question'** + String get question; } class _AppLocalizationsDelegate diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index d43de00..a855516 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -42,4 +42,10 @@ class AppLocalizationsEn extends AppLocalizations { @override String get start => 'Start'; + + @override + String get step => 'Step'; + + @override + String get question => 'Question'; }