From 471232acc540db1582d29cc3dfac8cdc759993fe Mon Sep 17 00:00:00 2001 From: AmirrezaChegini Date: Sat, 27 Sep 2025 12:54:45 +0330 Subject: [PATCH] add: question base feature --- lib/core/params/question_params.dart | 13 ++++++ .../data/datasource/question_datasource.dart | 28 +++++++++++++ .../question/data/model/question_model.dart | 13 ++++++ .../question_repository_impl.dart | 29 +++++++++++++ .../domain/entity/question_entity.dart | 14 +++++++ .../repository/question_repository.dart | 8 ++++ .../domain/usecases/get_question_usecase.dart | 17 ++++++++ .../presentation/bloc/question_bloc.dart | 41 +++++++++++++++++++ .../presentation/bloc/question_event.dart | 5 +++ .../presentation/bloc/question_state.dart | 15 +++++++ .../presentation/ui/question_page.dart | 10 +++++ lib/init_bindings.dart | 9 ++++ 12 files changed, 202 insertions(+) create mode 100644 lib/core/params/question_params.dart create mode 100644 lib/features/question/data/datasource/question_datasource.dart create mode 100644 lib/features/question/data/model/question_model.dart create mode 100644 lib/features/question/data/repository_impl/question_repository_impl.dart create mode 100644 lib/features/question/domain/entity/question_entity.dart create mode 100644 lib/features/question/domain/repository/question_repository.dart create mode 100644 lib/features/question/domain/usecases/get_question_usecase.dart create mode 100644 lib/features/question/presentation/bloc/question_bloc.dart create mode 100644 lib/features/question/presentation/bloc/question_event.dart create mode 100644 lib/features/question/presentation/bloc/question_state.dart create mode 100644 lib/features/question/presentation/ui/question_page.dart diff --git a/lib/core/params/question_params.dart b/lib/core/params/question_params.dart new file mode 100644 index 0000000..0c6fff1 --- /dev/null +++ b/lib/core/params/question_params.dart @@ -0,0 +1,13 @@ +class QuestionParams { + int? id; + + QuestionParams({this.id}); + + QuestionParams copyWith({ + int? id, + }) { + return QuestionParams( + id: id ?? this.id, + ); + } +} diff --git a/lib/features/question/data/datasource/question_datasource.dart b/lib/features/question/data/datasource/question_datasource.dart new file mode 100644 index 0000000..2142b93 --- /dev/null +++ b/lib/features/question/data/datasource/question_datasource.dart @@ -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 getData({required QuestionParams params}); +} + +class QuestionDatasourceImpl implements IQuestionDatasource { + final IHttpRequest httpRequest; + + const QuestionDatasourceImpl(this.httpRequest); + + @override + Future getData({required QuestionParams params}) async { + final response = await httpRequest.get( + path: MyApi.baseUrl, + ); + + return BaseResponse.getData( + response?['data'], + (json) => QuestionModel.fromJson(json), + ); + } +} diff --git a/lib/features/question/data/model/question_model.dart b/lib/features/question/data/model/question_model.dart new file mode 100644 index 0000000..489b66c --- /dev/null +++ b/lib/features/question/data/model/question_model.dart @@ -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 json) { + return QuestionModel( + id: json['id'], + ); + } +} diff --git a/lib/features/question/data/repository_impl/question_repository_impl.dart b/lib/features/question/data/repository_impl/question_repository_impl.dart new file mode 100644 index 0000000..af5a1c6 --- /dev/null +++ b/lib/features/question/data/repository_impl/question_repository_impl.dart @@ -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> 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')); + } + } + } +} diff --git a/lib/features/question/domain/entity/question_entity.dart b/lib/features/question/domain/entity/question_entity.dart new file mode 100644 index 0000000..377f2b1 --- /dev/null +++ b/lib/features/question/domain/entity/question_entity.dart @@ -0,0 +1,14 @@ +import 'package:equatable/equatable.dart'; + +class QuestionEntity extends Equatable { + final int? id; + + const QuestionEntity({ + this.id, + }); + + @override + List get props => [ + id, + ]; +} diff --git a/lib/features/question/domain/repository/question_repository.dart b/lib/features/question/domain/repository/question_repository.dart new file mode 100644 index 0000000..3c9e008 --- /dev/null +++ b/lib/features/question/domain/repository/question_repository.dart @@ -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> getData({required QuestionParams params}); +} diff --git a/lib/features/question/domain/usecases/get_question_usecase.dart b/lib/features/question/domain/usecases/get_question_usecase.dart new file mode 100644 index 0000000..697534b --- /dev/null +++ b/lib/features/question/domain/usecases/get_question_usecase.dart @@ -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 { + final IQuestionRepository repository; + + const GetQuestionUseCase(this.repository); + + @override + Future> call(QuestionParams params) { + return repository.getData(params: params); + } +} diff --git a/lib/features/question/presentation/bloc/question_bloc.dart b/lib/features/question/presentation/bloc/question_bloc.dart new file mode 100644 index 0000000..69823db --- /dev/null +++ b/lib/features/question/presentation/bloc/question_bloc.dart @@ -0,0 +1,41 @@ +import 'dart:async'; +import 'package:bloc/bloc.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'; + +class QuestionBloc extends Bloc { + /// ------------constructor------------ + QuestionBloc( + this._getQuestionUseCase, + ) : super(const QuestionState()) { + on(_getQuestionEvent); + } + + /// ------------UseCases------------ + final GetQuestionUseCase _getQuestionUseCase; + + /// ------------Variables------------ + + /// ------------Controllers------------ + + /// ------------Functions------------ + + /// ------------Api Calls------------ + FutureOr _getQuestionEvent(event, emit) async { + await _getQuestionUseCase(event.questionParams).then( + (value) { + value.fold( + (data) { + emit(state.copyWith(getQuestionStatus: BaseComplete(data))); + }, + (error) { + emit(state.copyWith(getQuestionStatus: BaseError(error.errorMessage))); + }, + ); + }, + ); + } +} diff --git a/lib/features/question/presentation/bloc/question_event.dart b/lib/features/question/presentation/bloc/question_event.dart new file mode 100644 index 0000000..b4c4b21 --- /dev/null +++ b/lib/features/question/presentation/bloc/question_event.dart @@ -0,0 +1,5 @@ +sealed class QuestionEvent { + const QuestionEvent(); +} + +class GetQuestionEvent extends QuestionEvent {} diff --git a/lib/features/question/presentation/bloc/question_state.dart b/lib/features/question/presentation/bloc/question_state.dart new file mode 100644 index 0000000..3c1b18f --- /dev/null +++ b/lib/features/question/presentation/bloc/question_state.dart @@ -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, + ); + } +} diff --git a/lib/features/question/presentation/ui/question_page.dart b/lib/features/question/presentation/ui/question_page.dart new file mode 100644 index 0000000..e4aa915 --- /dev/null +++ b/lib/features/question/presentation/ui/question_page.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class QuestionPage extends StatelessWidget { + const QuestionPage({super.key}); + + @override + Widget build(BuildContext context) { + return const Scaffold(); + } +} diff --git a/lib/init_bindings.dart b/lib/init_bindings.dart index faea518..8bb826b 100644 --- a/lib/init_bindings.dart +++ b/lib/init_bindings.dart @@ -4,6 +4,10 @@ import 'package:hadi_hoda_flutter/features/intro/data/datasource/intro_datasourc import 'package:hadi_hoda_flutter/features/intro/data/repository_impl/intro_repository_impl.dart'; import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart'; import 'package:hadi_hoda_flutter/features/intro/domain/usecases/get_intro_usecase.dart'; +import 'package:hadi_hoda_flutter/features/question/data/datasource/question_datasource.dart'; +import 'package:hadi_hoda_flutter/features/question/data/repository_impl/question_repository_impl.dart'; +import 'package:hadi_hoda_flutter/features/question/domain/repository/question_repository.dart'; +import 'package:hadi_hoda_flutter/features/question/domain/usecases/get_question_usecase.dart'; import 'package:hadi_hoda_flutter/features/sample/data/datasource/sample_datasource.dart'; import 'package:hadi_hoda_flutter/features/sample/data/repository_impl/sample_repository_impl.dart'; import 'package:hadi_hoda_flutter/features/sample/domain/repository/sample_repository.dart'; @@ -25,4 +29,9 @@ void initBindings() { locator.registerLazySingleton(() => IntroDatasourceImpl(locator())); locator.registerLazySingleton(() => IntroRepositoryImpl(locator())); locator.registerLazySingleton(() => GetIntroUseCase(locator())); + + /// Question Feature + locator.registerLazySingleton(() => QuestionDatasourceImpl(locator())); + locator.registerLazySingleton(() => QuestionRepositoryImpl(locator())); + locator.registerLazySingleton(() => GetQuestionUseCase(locator())); }