Browse Source

Merge pull request 'feature/database' (#9) from feature/database into develop

Reviewed-on: https://git.nwhco.ir/amirreza.chegini/hade_hoda_flutter/pulls/9
pull/10/head
amirreza.chegini 7 days ago
parent
commit
f59fd36939
  1. 1181
      assets/json/levels.json
  2. 2
      lib/common_ui/resources/my_assets.dart
  3. 8
      lib/common_ui/resources/my_text_style.dart
  4. 1
      lib/core/constants/my_constants.dart
  5. 2
      lib/core/middleware/auth_middleware.dart
  6. 13
      lib/core/params/home_params.dart
  7. 13
      lib/core/params/intro_params.dart
  8. 1
      lib/core/params/no_params.dart
  9. 18
      lib/core/routers/my_routes.dart
  10. 11
      lib/core/utils/my_scroll_behavior.dart
  11. 2
      lib/core/widgets/answer_box/styles/picture_box.dart
  12. 28
      lib/features/home/data/datasource/home_datasource.dart
  13. 13
      lib/features/home/data/model/home_model.dart
  14. 29
      lib/features/home/data/repository_impl/home_repository_impl.dart
  15. 4
      lib/features/home/domain/entity/home_entity.dart
  16. 8
      lib/features/home/domain/repository/home_repository.dart
  17. 17
      lib/features/home/domain/usecases/get_home_usecase.dart
  18. 47
      lib/features/home/presentation/bloc/home_bloc.dart
  19. 5
      lib/features/home/presentation/bloc/home_event.dart
  20. 15
      lib/features/home/presentation/bloc/home_state.dart
  21. 93
      lib/features/home/presentation/ui/home_page.dart
  22. 89
      lib/features/intro/data/datasource/intro_datasource.dart
  23. 13
      lib/features/intro/data/model/intro_model.dart
  24. 30
      lib/features/intro/data/repository_impl/intro_repository_impl.dart
  25. 7
      lib/features/intro/domain/repository/intro_repository.dart
  26. 16
      lib/features/intro/domain/usecases/extract_data_usecase.dart
  27. 17
      lib/features/intro/domain/usecases/get_intro_usecase.dart
  28. 11
      lib/features/intro/domain/usecases/loading_stream_usecase.dart
  29. 16
      lib/features/intro/domain/usecases/save_levels_usecase.dart
  30. 53
      lib/features/intro/presentation/bloc/intro_bloc.dart
  31. 3
      lib/features/intro/presentation/bloc/intro_event.dart
  32. 106
      lib/features/intro/presentation/ui/intro_page.dart
  33. 138
      lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart
  34. 31
      lib/features/level/data/datasource/level_datasource.dart
  35. 12
      lib/features/level/data/model/level_model.dart
  36. 10
      lib/features/level/data/repository_impl/level_repository_impl.dart
  37. 27
      lib/features/level/domain/entity/level_entity.dart
  38. 50
      lib/features/level/domain/entity/level_entity.g.dart
  39. 4
      lib/features/level/domain/entity/level_location.dart
  40. 2
      lib/features/level/domain/repository/level_repository.dart
  41. 6
      lib/features/level/domain/usecases/get_level_usecase.dart
  42. 69
      lib/features/level/presentation/bloc/level_bloc.dart
  43. 2
      lib/features/level/presentation/bloc/level_event.dart
  44. 13
      lib/features/level/presentation/ui/level_page.dart
  45. 2
      lib/features/level/presentation/ui/widgets/level_widget.dart
  46. 26
      lib/features/question/data/model/answer_model.dart
  47. 17
      lib/features/question/data/model/file_model.dart
  48. 23
      lib/features/question/data/model/question_model.dart
  49. 29
      lib/features/question/domain/entity/answer_entity.dart
  50. 56
      lib/features/question/domain/entity/answer_entity.g.dart
  51. 19
      lib/features/question/domain/entity/file_entity.dart
  52. 47
      lib/features/question/domain/entity/file_entity.g.dart
  53. 40
      lib/features/question/domain/entity/question_entity.dart
  54. 62
      lib/features/question/domain/entity/question_entity.g.dart
  55. 40
      lib/init_bindings.dart
  56. 3
      lib/main.dart
  57. 314
      pubspec.lock
  58. 10
      pubspec.yaml

1181
assets/json/levels.json
File diff suppressed because it is too large
View File

2
lib/common_ui/resources/my_assets.dart

@ -3,7 +3,7 @@ class MyAssets {
const MyAssets._internal(); const MyAssets._internal();
factory MyAssets() => _i; factory MyAssets() => _i;
static const String backgroundIntro = 'assets/images/background_intro.png';
static const String backgroundHome = 'assets/images/background_intro.png';
static const String closeBtn = 'assets/images/close_btn.svg'; static const String closeBtn = 'assets/images/close_btn.svg';
static const String hadiHoda = 'assets/images/hadi_hoda.png'; static const String hadiHoda = 'assets/images/hadi_hoda.png';
static const String musicOff = 'assets/images/music_off.svg'; static const String musicOff = 'assets/images/music_off.svg';

8
lib/common_ui/resources/my_text_style.dart

@ -7,7 +7,7 @@ class MyTextStyle {
static const String fontFamily = 'dinokids'; static const String fontFamily = 'dinokids';
static const TextStyle normal = TextStyle(
static const TextStyle normal26 = TextStyle(
fontFamily: fontFamily, fontFamily: fontFamily,
fontSize: 26, fontSize: 26,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
@ -19,4 +19,10 @@ class MyTextStyle {
), ),
] ]
); );
static const TextStyle normal17 = TextStyle(
fontFamily: fontFamily,
fontSize: 17,
fontWeight: FontWeight.w400,
);
} }

1
lib/core/constants/my_constants.dart

@ -5,4 +5,5 @@ class MyConstants {
static const String token = 'TOKEN'; static const String token = 'TOKEN';
static const String theme = 'THEME'; static const String theme = 'THEME';
static const String levelBox = 'LEVEL_BOX';
} }

2
lib/core/middleware/auth_middleware.dart

@ -12,7 +12,7 @@ class AuthMiddleware {
static FutureOr<String?> redirect(BuildContext context, GoRouterState state) async { static FutureOr<String?> redirect(BuildContext context, GoRouterState state) async {
if (AuthStorage.isLogin()) { if (AuthStorage.isLogin()) {
return Routes.introPage;
return Routes.homePage;
} else { } else {
return null; return null;
} }

13
lib/core/params/home_params.dart

@ -0,0 +1,13 @@
class HomeParams {
int? id;
HomeParams({this.id});
HomeParams copyWith({
int? id,
}) {
return HomeParams(
id: id ?? this.id,
);
}
}

13
lib/core/params/intro_params.dart

@ -1,13 +0,0 @@
class IntroParams {
int? id;
IntroParams({this.id});
IntroParams copyWith({
int? id,
}) {
return IntroParams(
id: id ?? this.id,
);
}
}

1
lib/core/params/no_params.dart

@ -0,0 +1 @@
class NoParams {}

18
lib/core/routers/my_routes.dart

@ -1,9 +1,12 @@
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hadi_hoda_flutter/core/utils/context_provider.dart'; import 'package:hadi_hoda_flutter/core/utils/context_provider.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_bloc.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/ui/home_page.dart';
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_bloc.dart'; import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_bloc.dart';
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/intro_page.dart'; import 'package:hadi_hoda_flutter/features/intro/presentation/ui/intro_page.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_bloc.dart'; import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_bloc.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_event.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/ui/level_page.dart'; import 'package:hadi_hoda_flutter/features/level/presentation/ui/level_page.dart';
import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_bloc.dart'; import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_bloc.dart';
import 'package:hadi_hoda_flutter/features/question/presentation/ui/question_page.dart'; import 'package:hadi_hoda_flutter/features/question/presentation/ui/question_page.dart';
@ -15,27 +18,36 @@ class Routes {
factory Routes() => _i; factory Routes() => _i;
static const String introPage = '/intro_page'; static const String introPage = '/intro_page';
static const String homePage = '/home_page';
static const String questionPage = '/question_page'; static const String questionPage = '/question_page';
static const String levelPage = '/level_page'; static const String levelPage = '/level_page';
} }
GoRouter get appPages => GoRouter( GoRouter get appPages => GoRouter(
initialLocation: Routes.levelPage,
initialLocation: Routes.introPage,
navigatorKey: ContextProvider.navigatorKey, navigatorKey: ContextProvider.navigatorKey,
routes: [ routes: [
GoRoute( GoRoute(
name: Routes.introPage, name: Routes.introPage,
path: Routes.introPage, path: Routes.introPage,
builder: (context, state) => BlocProvider( builder: (context, state) => BlocProvider(
create: (context) => IntroBloc(locator()),
create: (context) => IntroBloc(locator(), locator(), locator()),
child: const IntroPage(), child: const IntroPage(),
), ),
), ),
GoRoute(
name: Routes.homePage,
path: Routes.homePage,
builder: (context, state) => BlocProvider(
create: (context) => HomeBloc(locator()),
child: const HomePage(),
),
),
GoRoute( GoRoute(
name: Routes.levelPage, name: Routes.levelPage,
path: Routes.levelPage, path: Routes.levelPage,
builder: (context, state) => BlocProvider( builder: (context, state) => BlocProvider(
create: (context) => LevelBloc(locator()),
create: (context) => LevelBloc(locator())..add(GetLevelListEvent()),
child: const LevelPage(), child: const LevelPage(),
), ),
), ),

11
lib/core/utils/my_scroll_behavior.dart

@ -1,11 +0,0 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
class MyScrollBehavior extends ScrollBehavior {
@override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
PointerDeviceKind.trackpad,
};
}

2
lib/core/widgets/answer_box/styles/picture_box.dart

@ -20,7 +20,7 @@ class AnswerPictureBox extends StatelessWidget {
child: Stack( child: Stack(
children: [ children: [
MyImage( MyImage(
image: MyAssets.backgroundIntro,
image: MyAssets.backgroundHome,
fit: BoxFit.cover, fit: BoxFit.cover,
size: 170, size: 170,
), ),

28
lib/features/home/data/datasource/home_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/home_params.dart';
import 'package:hadi_hoda_flutter/core/response/base_response.dart';
import 'package:hadi_hoda_flutter/features/home/data/model/home_model.dart';
import 'package:hadi_hoda_flutter/features/home/domain/entity/home_entity.dart';
abstract class IHomeDatasource {
Future<HomeEntity> getData({required HomeParams params});
}
class HomeDatasourceImpl implements IHomeDatasource {
final IHttpRequest httpRequest;
const HomeDatasourceImpl(this.httpRequest);
@override
Future<HomeEntity> getData({required HomeParams params}) async {
final response = await httpRequest.get(
path: MyApi.baseUrl,
);
return BaseResponse.getData<HomeEntity>(
response?['data'],
(json) => HomeModel.fromJson(json),
);
}
}

13
lib/features/home/data/model/home_model.dart

@ -0,0 +1,13 @@
import 'package:hadi_hoda_flutter/features/home/domain/entity/home_entity.dart';
class HomeModel extends HomeEntity {
const HomeModel({
super.id,
});
factory HomeModel.fromJson(Map<String, dynamic> json) {
return HomeModel(
id: json['id'],
);
}
}

29
lib/features/home/data/repository_impl/home_repository_impl.dart

@ -0,0 +1,29 @@
import 'package:hadi_hoda_flutter/core/params/home_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/home/data/datasource/home_datasource.dart';
import 'package:hadi_hoda_flutter/features/home/domain/entity/home_entity.dart';
import 'package:hadi_hoda_flutter/features/home/domain/repository/home_repository.dart';
class HomeRepositoryImpl implements IHomeRepository {
final IHomeDatasource datasource;
const HomeRepositoryImpl(this.datasource);
@override
Future<DataState<HomeEntity, MyException>> getData({required HomeParams params}) async {
try {
final HomeEntity 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'));
}
}
}
}

4
lib/features/intro/domain/entity/intro_entity.dart → lib/features/home/domain/entity/home_entity.dart

@ -1,9 +1,9 @@
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
class IntroEntity extends Equatable {
class HomeEntity extends Equatable {
final int? id; final int? id;
const IntroEntity({
const HomeEntity({
this.id, this.id,
}); });

8
lib/features/home/domain/repository/home_repository.dart

@ -0,0 +1,8 @@
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart';
import 'package:hadi_hoda_flutter/core/params/home_params.dart';
import 'package:hadi_hoda_flutter/core/utils/data_state.dart';
import 'package:hadi_hoda_flutter/features/home/domain/entity/home_entity.dart';
abstract class IHomeRepository {
Future<DataState<HomeEntity, MyException>> getData({required HomeParams params});
}

17
lib/features/home/domain/usecases/get_home_usecase.dart

@ -0,0 +1,17 @@
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart';
import 'package:hadi_hoda_flutter/core/params/home_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/home/domain/entity/home_entity.dart';
import 'package:hadi_hoda_flutter/features/home/domain/repository/home_repository.dart';
class GetHomeUseCase implements UseCase<HomeEntity, HomeParams> {
final IHomeRepository repository;
const GetHomeUseCase(this.repository);
@override
Future<DataState<HomeEntity, MyException>> call(HomeParams params) {
return repository.getData(params: params);
}
}

47
lib/features/home/presentation/bloc/home_bloc.dart

@ -0,0 +1,47 @@
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:flutter/cupertino.dart';
import 'package:go_router/go_router.dart';
import 'package:hadi_hoda_flutter/core/routers/my_routes.dart';
import 'package:hadi_hoda_flutter/core/status/base_status.dart';
import 'package:hadi_hoda_flutter/features/home/domain/entity/home_entity.dart';
import 'package:hadi_hoda_flutter/features/home/domain/usecases/get_home_usecase.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_event.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_state.dart';
class HomeBloc extends Bloc<HomeEvent, HomeState> {
/// ------------constructor------------
HomeBloc(
this._getHomeUseCase,
) : super(const HomeState()) {
on<GetHomeEvent>(_getHomeEvent);
}
/// ------------UseCases------------
final GetHomeUseCase _getHomeUseCase;
/// ------------Variables------------
/// ------------Controllers------------
/// ------------Functions------------
void goToLevelPage(BuildContext context){
context.pushNamed(Routes.levelPage);
}
/// ------------Api Calls------------
FutureOr<void> _getHomeEvent(event, emit) async {
await _getHomeUseCase(event.homeParams).then(
(value) {
value.fold(
(data) {
emit(state.copyWith(getHomeStatus: BaseComplete<HomeEntity>(data)));
},
(error) {
emit(state.copyWith(getHomeStatus: BaseError(error.errorMessage)));
},
);
},
);
}
}

5
lib/features/home/presentation/bloc/home_event.dart

@ -0,0 +1,5 @@
sealed class HomeEvent {
const HomeEvent();
}
class GetHomeEvent extends HomeEvent {}

15
lib/features/home/presentation/bloc/home_state.dart

@ -0,0 +1,15 @@
import 'package:hadi_hoda_flutter/core/status/base_status.dart';
class HomeState {
final BaseStatus getHomeStatus;
const HomeState({this.getHomeStatus = const BaseInit()});
HomeState copyWith({
BaseStatus? getHomeStatus,
}) {
return HomeState(
getHomeStatus: getHomeStatus ?? this.getHomeStatus,
);
}
}

93
lib/features/home/presentation/ui/home_page.dart

@ -0,0 +1,93 @@
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/theme/my_theme.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/utils/screen_size.dart';
import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_bloc.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(backgroundColor: context.noColor),
extendBodyBehindAppBar: true,
body: DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(MyAssets.backgroundHome),
fit: BoxFit.cover,
),
),
child: SizedBox(
width: context.widthScreen,
height: context.heightScreen,
child: Stack(
alignment: Alignment.center,
children: [
_music(context),
_name(context),
_bottomBtns(context),
],
),
),
),
);
}
Positioned _music(BuildContext context) {
return Positioned(
top: checkSize(context: context, mobile: 36, tablet: 60),
right: checkSize(context: context, mobile: 16, tablet: 30),
child: MyImage(
image: MyAssets.musicOn,
size: checkSize(context: context, tablet: 120),
),
);
}
Positioned _name(BuildContext context) {
return Positioned(
top: 146,
child: MyImage(
image: MyAssets.hadiHoda,
size: checkSize(context: context, mobile: 232, tablet: 400),
fit: BoxFit.cover,
),
);
}
Positioned _bottomBtns(BuildContext context) {
return Positioned(
bottom: checkSize(context: context, mobile: 40, tablet: 60),
left: checkSize(context: context, mobile: 16, tablet: 30),
right: checkSize(context: context, mobile: 16, tablet: 30),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MyImage(
image: MyAssets.language,
size: checkSize(context: context, tablet: 120),
),
InkWell(
child: MyImage(
image: MyAssets.start,
size: checkSize(context: context, mobile: 90, tablet: 160),
),
onTap: () =>
BlocProvider.of<HomeBloc>(context).goToLevelPage(context),
),
MyImage(
image: MyAssets.theme,
size: checkSize(context: context, tablet: 120),
),
],
),
);
}
}

89
lib/features/intro/data/datasource/intro_datasource.dart

@ -1,28 +1,93 @@
import 'package:hadi_hoda_flutter/core/constants/my_api.dart';
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter_archive/flutter_archive.dart';
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/network/http_request.dart';
import 'package:hadi_hoda_flutter/core/params/intro_params.dart';
import 'package:hadi_hoda_flutter/core/response/base_response.dart'; import 'package:hadi_hoda_flutter/core/response/base_response.dart';
import 'package:hadi_hoda_flutter/features/intro/data/model/intro_model.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/entity/intro_entity.dart';
import 'package:hadi_hoda_flutter/features/level/data/model/level_model.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart';
abstract class IIntroDatasource { abstract class IIntroDatasource {
Future<IntroEntity> getData({required IntroParams params});
Future<void> saveLevels();
Future<void> extractData();
Stream<double> loadingStream();
} }
class IntroDatasourceImpl implements IIntroDatasource { class IntroDatasourceImpl implements IIntroDatasource {
final IHttpRequest httpRequest; final IHttpRequest httpRequest;
final StreamController<double> streamController = StreamController<double>.broadcast();
IntroDatasourceImpl(this.httpRequest);
const IntroDatasourceImpl(this.httpRequest);
@override
Future<void> saveLevels() async {
try {
final Box<LevelEntity> levelBox = Hive.box(MyConstants.levelBox);
if (levelBox.isEmpty) {
final String levelAssets = await rootBundle.loadString(
'assets/json/levels.json',
);
final dynamic response = jsonDecode(levelAssets);
final List<LevelEntity> levelList = BaseResponse.getDataList<LevelEntity>(
response?['result'],
(json) => LevelModel.fromJson(json),
);
await levelBox.addAll(levelList);
}
} catch (e) {
throw MyException(errorMessage: '$e');
}
}
@override @override
Future<IntroEntity> getData({required IntroParams params}) async {
final response = await httpRequest.get(
path: MyApi.baseUrl,
Future<void> extractData() async {
try {
final Directory dir = await getApplicationDocumentsDirectory();
final File file = File('${dir.path}/data.zip');
if (!(await file.exists())) {
final ByteData assetFile = await rootBundle.load('assets/data/data.zip');
await file.create(recursive: true);
await file.writeAsBytes(
assetFile.buffer.asUint8List(
assetFile.offsetInBytes,
assetFile.lengthInBytes,
),
flush: true,
); );
return BaseResponse.getData<IntroEntity>(
response?['data'],
(json) => IntroModel.fromJson(json),
await ZipFile.extractToDirectory(
zipFile: file,
destinationDir: dir,
onExtracting: (zipEntry, progress) {
streamController.add(progress);
return ZipFileOperation.includeItem;
},
); );
} else {
streamController.add(20);
await Future.delayed(Duration(milliseconds: 150));
streamController.add(40);
await Future.delayed(Duration(milliseconds: 150));
streamController.add(60);
await Future.delayed(Duration(milliseconds: 150));
streamController.add(80);
await Future.delayed(Duration(milliseconds: 150));
streamController.add(100);
await Future.delayed(Duration(milliseconds: 150));
}
} catch (e) {
throw MyException(errorMessage: '$e');
} }
} }
@override
Stream<double> loadingStream() => streamController.stream;
}

13
lib/features/intro/data/model/intro_model.dart

@ -1,13 +0,0 @@
import 'package:hadi_hoda_flutter/features/intro/domain/entity/intro_entity.dart';
class IntroModel extends IntroEntity {
const IntroModel({
super.id,
});
factory IntroModel.fromJson(Map<String, dynamic> json) {
return IntroModel(
id: json['id'],
);
}
}

30
lib/features/intro/data/repository_impl/intro_repository_impl.dart

@ -1,9 +1,8 @@
import 'package:hadi_hoda_flutter/core/params/intro_params.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart';
import 'package:hadi_hoda_flutter/core/params/no_params.dart';
import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; import 'package:hadi_hoda_flutter/core/utils/data_state.dart';
import 'package:hadi_hoda_flutter/features/intro/data/datasource/intro_datasource.dart'; import 'package:hadi_hoda_flutter/features/intro/data/datasource/intro_datasource.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/entity/intro_entity.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart'; import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart';
class IntroRepositoryImpl implements IIntroRepository { class IntroRepositoryImpl implements IIntroRepository {
@ -12,10 +11,10 @@ class IntroRepositoryImpl implements IIntroRepository {
const IntroRepositoryImpl(this.datasource); const IntroRepositoryImpl(this.datasource);
@override @override
Future<DataState<IntroEntity, MyException>> getData({required IntroParams params}) async {
Future<DataState<NoParams, MyException>> saveLevels() async {
try { try {
final IntroEntity response = await datasource.getData(params: params);
return DataState.success(response);
await datasource.saveLevels();
return DataState.success(NoParams());
} on MyException catch (e) { } on MyException catch (e) {
return DataState.error(e); return DataState.error(e);
} catch (e) { } catch (e) {
@ -26,4 +25,25 @@ class IntroRepositoryImpl implements IIntroRepository {
} }
} }
} }
@override
Future<DataState<NoParams, MyException>> extractData() async {
try {
await datasource.extractData();
return DataState.success(NoParams());
} on MyException catch (e) {
return DataState.error(e);
} catch (e) {
if (kDebugMode) {
rethrow;
} else {
return DataState.error(MyException(errorMessage: '$e'));
}
}
}
@override
Stream<double> loadingStream() {
return datasource.loadingStream();
}
} }

7
lib/features/intro/domain/repository/intro_repository.dart

@ -1,8 +1,9 @@
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart';
import 'package:hadi_hoda_flutter/core/params/intro_params.dart';
import 'package:hadi_hoda_flutter/core/params/no_params.dart';
import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; import 'package:hadi_hoda_flutter/core/utils/data_state.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/entity/intro_entity.dart';
abstract class IIntroRepository { abstract class IIntroRepository {
Future<DataState<IntroEntity, MyException>> getData({required IntroParams params});
Future<DataState<NoParams, MyException>> saveLevels();
Future<DataState<NoParams, MyException>> extractData();
Stream<double> loadingStream();
} }

16
lib/features/intro/domain/usecases/extract_data_usecase.dart

@ -0,0 +1,16 @@
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart';
import 'package:hadi_hoda_flutter/core/params/no_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/intro/domain/repository/intro_repository.dart';
class ExtractDataUseCase implements UseCase<NoParams, NoParams> {
final IIntroRepository repository;
const ExtractDataUseCase(this.repository);
@override
Future<DataState<NoParams, MyException>> call(NoParams params) {
return repository.extractData();
}
}

17
lib/features/intro/domain/usecases/get_intro_usecase.dart

@ -1,17 +0,0 @@
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart';
import 'package:hadi_hoda_flutter/core/params/intro_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/intro/domain/entity/intro_entity.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart';
class GetIntroUseCase implements UseCase<IntroEntity, IntroParams> {
final IIntroRepository repository;
const GetIntroUseCase(this.repository);
@override
Future<DataState<IntroEntity, MyException>> call(IntroParams params) {
return repository.getData(params: params);
}
}

11
lib/features/intro/domain/usecases/loading_stream_usecase.dart

@ -0,0 +1,11 @@
import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart';
class LoadingStreamUseCase {
final IIntroRepository repository;
const LoadingStreamUseCase(this.repository);
Stream<double> call() {
return repository.loadingStream();
}
}

16
lib/features/intro/domain/usecases/save_levels_usecase.dart

@ -0,0 +1,16 @@
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart';
import 'package:hadi_hoda_flutter/core/params/no_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/intro/domain/repository/intro_repository.dart';
class SaveLevelsUseCase implements UseCase<NoParams, NoParams> {
final IIntroRepository repository;
const SaveLevelsUseCase(this.repository);
@override
Future<DataState<NoParams, MyException>> call(NoParams params) {
return repository.saveLevels();
}
}

53
lib/features/intro/presentation/bloc/intro_bloc.dart

@ -1,38 +1,69 @@
import 'dart:async'; import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:hadi_hoda_flutter/core/status/base_status.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/entity/intro_entity.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/get_intro_usecase.dart';
import 'package:go_router/go_router.dart';
import 'package:hadi_hoda_flutter/core/params/no_params.dart';
import 'package:hadi_hoda_flutter/core/routers/my_routes.dart';
import 'package:hadi_hoda_flutter/core/utils/context_provider.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/extract_data_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/loading_stream_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/save_levels_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_event.dart'; import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_event.dart';
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_state.dart'; import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_state.dart';
class IntroBloc extends Bloc<IntroEvent, IntroState> { class IntroBloc extends Bloc<IntroEvent, IntroState> {
/// ------------constructor------------ /// ------------constructor------------
IntroBloc( IntroBloc(
this._getIntroUseCase,
this._saveLevelsUseCase,
this._extractDataUseCase,
this._loadingStreamUseCase,
) : super(const IntroState()) { ) : super(const IntroState()) {
on<GetIntroEvent>(_getIntroEvent);
on<SaveLevelsEvent>(_saveLevelsEvent);
on<ExtractDataEvent>(_extractDataEvent);
loadingStream = _loadingStreamUseCase();
} }
/// ------------UseCases------------ /// ------------UseCases------------
final GetIntroUseCase _getIntroUseCase;
final SaveLevelsUseCase _saveLevelsUseCase;
final ExtractDataUseCase _extractDataUseCase;
final LoadingStreamUseCase _loadingStreamUseCase;
/// ------------Variables------------ /// ------------Variables------------
Stream<double> loadingStream = Stream.empty();
/// ------------Controllers------------ /// ------------Controllers------------
/// ------------Functions------------ /// ------------Functions------------
/// ------------Api Calls------------ /// ------------Api Calls------------
FutureOr<void> _getIntroEvent(event, emit) async {
await _getIntroUseCase(event.introParams).then(
FutureOr<void> _saveLevelsEvent(SaveLevelsEvent event,
Emitter<IntroState> emit) async {
await _saveLevelsUseCase(NoParams()).then(
(value) { (value) {
value.fold( value.fold(
(data) {
emit(state.copyWith(getIntroStatus: BaseComplete<IntroEntity>(data)));
(data) async {
add(ExtractDataEvent());
},
(error) {},
);
},
);
}
FutureOr<void> _extractDataEvent(ExtractDataEvent event,
Emitter<IntroState> emit) async {
await _extractDataUseCase(NoParams()).then(
(value) {
value.fold(
(data) async {
await Future.delayed(
Duration(seconds: 1), () {
ContextProvider.context!.goNamed(Routes.homePage);
},
);
}, },
(error) { (error) {
emit(state.copyWith(getIntroStatus: BaseError(error.errorMessage)));
print(error.errorMessage);
}, },
); );
}, },

3
lib/features/intro/presentation/bloc/intro_event.dart

@ -2,4 +2,5 @@ sealed class IntroEvent {
const IntroEvent(); const IntroEvent();
} }
class GetIntroEvent extends IntroEvent {}
class SaveLevelsEvent extends IntroEvent {}
class ExtractDataEvent extends IntroEvent {}

106
lib/features/intro/presentation/ui/intro_page.dart

@ -1,93 +1,65 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.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_assets.dart';
import 'package:hadi_hoda_flutter/common_ui/theme/my_theme.dart';
import 'package:hadi_hoda_flutter/core/routers/my_routes.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/utils/my_image.dart';
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; import 'package:hadi_hoda_flutter/core/utils/screen_size.dart';
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_bloc.dart';
import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_event.dart';
import 'package:hadi_hoda_flutter/features/intro/presentation/ui/widgets/intro_loading_widget.dart';
class IntroPage extends StatelessWidget {
class IntroPage extends StatefulWidget {
const IntroPage({super.key}); const IntroPage({super.key});
@override
State<IntroPage> createState() => _IntroPageState();
}
class _IntroPageState extends State<IntroPage> {
@override
void initState() {
super.initState();
context.read<IntroBloc>().add(SaveLevelsEvent());
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(backgroundColor: context.noColor),
extendBodyBehindAppBar: true,
body: DecoratedBox(
body: Container(
height: context.heightScreen,
width: context.widthScreen,
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0XFF00154C), Color(0XFF150532)],
),
image: DecorationImage( image: DecorationImage(
image: AssetImage(MyAssets.backgroundIntro),
fit: BoxFit.cover,
image: AssetImage(MyAssets.pattern),
scale: 3,
repeat: ImageRepeat.repeat,
colorFilter: ColorFilter.mode(
Colors.white.withValues(alpha: 0.2),
BlendMode.srcIn,
),
), ),
), ),
child: SizedBox(
width: context.widthScreen,
height: context.heightScreen,
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [
_music(context),
_name(context),
_bottomBtns(context),
],
),
children: [_image(), _loading(context)],
), ),
), ),
); );
} }
Positioned _music(BuildContext context) {
return Positioned(
top: checkSize(context: context, mobile: 36, tablet: 60),
right: checkSize(context: context, mobile: 16, tablet: 30),
child: MyImage(
image: MyAssets.musicOn,
size: checkSize(context: context, tablet: 120),
),
);
}
Positioned _name(BuildContext context) {
return Positioned(
top: 146,
child: MyImage(
image: MyAssets.hadiHoda,
size: checkSize(context: context, mobile: 232, tablet: 400),
fit: BoxFit.cover,
),
);
}
MyImage _image() => MyImage(image: MyAssets.hadiHoda, size: 200);
Positioned _bottomBtns(BuildContext context) {
Positioned _loading(BuildContext context) {
return Positioned( return Positioned(
bottom: checkSize(context: context, mobile: 40, tablet: 60),
left: checkSize(context: context, mobile: 16, tablet: 30),
right: checkSize(context: context, mobile: 16, tablet: 30),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MyImage(
image: MyAssets.language,
size: checkSize(context: context, tablet: 120),
),
InkWell(
child: MyImage(
image: MyAssets.start,
size: checkSize(context: context, mobile: 90, tablet: 160),
),
onTap: () {
context.goNamed(Routes.questionPage);
},
),
MyImage(
image: MyAssets.theme,
size: checkSize(context: context, tablet: 120),
),
],
bottom: MediaQuery.viewPaddingOf(context).bottom,
child: IntroLoadingWidget(
percent: 80,
loadingStream: context.read<IntroBloc>().loadingStream,
), ),
); );
} }

138
lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart

@ -0,0 +1,138 @@
import 'package:flutter/material.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
class IntroLoadingWidget extends StatelessWidget {
const IntroLoadingWidget({
super.key,
this.percent,
this.loadingStream,
});
final double? percent;
final Stream<double>? loadingStream;
@override
Widget build(BuildContext context) {
return ClipPath(
clipper: BubbleClip(),
child: Container(
width: 300,
height: 60,
padding: EdgeInsets.symmetric(
vertical: MySpaces.s4,
horizontal: MySpaces.s2,
),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment(0, 1), // bottom
end: Alignment(0, -1), // top
colors: [
Color(0xFFCADCFF), // #CADCFF
Colors.white, // #FFFFFF
],
),
),
child: StreamBuilder<double>(
initialData: 0,
stream: loadingStream,
builder: (context, snapshot) {
print(snapshot.data);
return Row(
children: [
Expanded(
flex: 85,
child: ClipPath(
clipper: BubbleClip(),
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
padding: EdgeInsetsDirectional.only(
end: 260 - ((snapshot.data ?? 0) * 260 / 100),
),
decoration: BoxDecoration(
color: Color(0xFF1F59BD).withValues(alpha: 0.25),
),
child: ClipPath(
clipper: BubbleClip(),
child: Container(
decoration: BoxDecoration(
gradient: RadialGradient(
radius: 2,
colors: [
Color(0xFFFFBD00), // #CADCFF
Color(0xFFFF772C), // #CADCFF
],
),
),
),
),
),
),
),
Expanded(
flex: 15,
child: Center(
child: Text(
'${snapshot.data?.toInt() ?? 0}%',
style: MyTextStyle.normal17.copyWith(
color: Color(0XFF6E83A8),
),
),
),
),
],
);
}
),
),
);
}
}
class BubbleClip extends CustomClipper<Path> {
@override
Path getClip(Size size) {
// Original SVG viewBox: 334 x 60
const double w0 = 334.0;
const double h0 = 60.0;
final sx = size.width / w0;
final sy = size.height / h0;
// SVG path:
// M9.82057 10.3597
// C -1.70838 17.1589 -3.47995 44.4301 6.60447 53.1719
// C 16.0075 61.291 305.076 61.9385 323.201 53.4956
// C 341.326 45.0527 332.116 8.04571 324.829 5.7273
// C 307.985 -2.06805 28.6539 -0.77294 9.82057 10.3597
// Z
final p = Path()
..moveTo(9.82057 * sx, 10.3597 * sy)
..cubicTo(
-1.70838 * sx, 17.1589 * sy,
-3.47995 * sx, 44.4301 * sy,
6.60447 * sx, 53.1719 * sy,
)
..cubicTo(
16.0075 * sx, 61.291 * sy,
305.076 * sx, 61.9385 * sy,
323.201 * sx, 53.4956 * sy,
)
..cubicTo(
341.326 * sx, 45.0527 * sy,
332.116 * sx, 8.04571 * sy,
324.829 * sx, 5.7273 * sy,
)
..cubicTo(
307.985 * sx, -2.06805 * sy,
28.6539 * sx, -0.77294 * sy,
9.82057 * sx, 10.3597 * sy,
)
..close();
return p;
}
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}

31
lib/features/level/data/datasource/level_datasource.dart

@ -1,28 +1,49 @@
import 'package:hadi_hoda_flutter/core/constants/my_api.dart'; import 'package:hadi_hoda_flutter/core/constants/my_api.dart';
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/network/http_request.dart';
import 'package:hadi_hoda_flutter/core/params/level_params.dart'; import 'package:hadi_hoda_flutter/core/params/level_params.dart';
import 'package:hadi_hoda_flutter/core/response/base_response.dart'; import 'package:hadi_hoda_flutter/core/response/base_response.dart';
import 'package:hadi_hoda_flutter/features/level/data/model/level_model.dart'; import 'package:hadi_hoda_flutter/features/level/data/model/level_model.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
import 'package:hive/hive.dart';
abstract class ILevelDatasource { abstract class ILevelDatasource {
Future<LevelEntity> getData({required LevelParams params});
Future<List<LevelEntity>> getLevels({required LevelParams params});
} }
class LevelDatasourceImpl implements ILevelDatasource {
/// Remote
class RemoteLevelDatasourceImpl implements ILevelDatasource {
final IHttpRequest httpRequest; final IHttpRequest httpRequest;
const LevelDatasourceImpl(this.httpRequest);
const RemoteLevelDatasourceImpl(this.httpRequest);
@override @override
Future<LevelEntity> getData({required LevelParams params}) async {
Future<List<LevelEntity>> getLevels({required LevelParams params}) async {
final response = await httpRequest.get( final response = await httpRequest.get(
path: MyApi.baseUrl, path: MyApi.baseUrl,
); );
return BaseResponse.getData<LevelEntity>(
return BaseResponse.getDataList<LevelEntity>(
response?['data'], response?['data'],
(json) => LevelModel.fromJson(json), (json) => LevelModel.fromJson(json),
); );
} }
} }
/// Local
class LocalLevelDatasourceImpl implements ILevelDatasource {
const LocalLevelDatasourceImpl();
@override
Future<List<LevelEntity>> getLevels({required LevelParams params}) async {
try {
final Box<LevelEntity> levelBox = Hive.box(MyConstants.levelBox);
return levelBox.values.toList();
} catch (_) {
throw MyException(errorMessage: 'Operation Failed');
}
}
}

12
lib/features/level/data/model/level_model.dart

@ -1,13 +1,23 @@
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.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';
class LevelModel extends LevelEntity { class LevelModel extends LevelEntity {
const LevelModel({
LevelModel({
super.id, super.id,
super.order,
super.title,
super.questions,
}); });
factory LevelModel.fromJson(Map<String, dynamic> json) { factory LevelModel.fromJson(Map<String, dynamic> json) {
return LevelModel( return LevelModel(
id: json['id'], id: json['id'],
order: json['order'],
title: json['title'],
questions: json['questions']
?.map<QuestionEntity>((e) => QuestionModel.fromJson(e))
.toList(),
); );
} }
} }

10
lib/features/level/data/repository_impl/level_repository_impl.dart

@ -1,6 +1,6 @@
import 'package:hadi_hoda_flutter/core/params/level_params.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart'; import 'package:hadi_hoda_flutter/core/error_handler/my_exception.dart';
import 'package:hadi_hoda_flutter/core/params/level_params.dart';
import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; import 'package:hadi_hoda_flutter/core/utils/data_state.dart';
import 'package:hadi_hoda_flutter/features/level/data/datasource/level_datasource.dart'; import 'package:hadi_hoda_flutter/features/level/data/datasource/level_datasource.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
@ -12,9 +12,13 @@ class LevelRepositoryImpl implements ILevelRepository {
const LevelRepositoryImpl(this.datasource); const LevelRepositoryImpl(this.datasource);
@override @override
Future<DataState<LevelEntity, MyException>> getData({required LevelParams params}) async {
Future<DataState<List<LevelEntity>, MyException>> getLevels({
required LevelParams params,
}) async {
try { try {
final LevelEntity response = await datasource.getData(params: params);
final List<LevelEntity> response = await datasource.getLevels(
params: params,
);
return DataState.success(response); return DataState.success(response);
} on MyException catch (e) { } on MyException catch (e) {
return DataState.error(e); return DataState.error(e);

27
lib/features/level/domain/entity/level_entity.dart

@ -1,14 +1,23 @@
import 'package:equatable/equatable.dart';
import 'package:hadi_hoda_flutter/features/question/domain/entity/question_entity.dart';
import 'package:hive/hive.dart';
class LevelEntity extends Equatable {
final int? id;
part 'level_entity.g.dart';
const LevelEntity({
@HiveType(typeId: 0)
class LevelEntity extends HiveObject {
@HiveField(0)
int? id;
@HiveField(1)
int? order;
@HiveField(2)
String? title;
@HiveField(3)
List<QuestionEntity>? questions;
LevelEntity({
this.id, this.id,
this.order,
this.title,
this.questions,
}); });
@override
List<Object?> get props => [
id,
];
} }

50
lib/features/level/domain/entity/level_entity.g.dart

@ -0,0 +1,50 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'level_entity.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class LevelEntityAdapter extends TypeAdapter<LevelEntity> {
@override
final int typeId = 0;
@override
LevelEntity read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return LevelEntity(
id: fields[0] as int?,
order: fields[1] as int?,
title: fields[2] as String?,
questions: (fields[3] as List?)?.cast<QuestionEntity>(),
);
}
@override
void write(BinaryWriter writer, LevelEntity obj) {
writer
..writeByte(4)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.order)
..writeByte(2)
..write(obj.title)
..writeByte(3)
..write(obj.questions);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is LevelEntityAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

4
lib/features/level/domain/entity/level_location.dart

@ -1,11 +1,11 @@
class MissionLocation {
class LevelLocation {
final int? index; final int? index;
final double? top; final double? top;
final double? bottom; final double? bottom;
final double? right; final double? right;
final double? left; final double? left;
const MissionLocation({
const LevelLocation({
this.index, this.index,
this.top, this.top,
this.bottom, this.bottom,

2
lib/features/level/domain/repository/level_repository.dart

@ -4,5 +4,5 @@ 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/level/domain/entity/level_entity.dart';
abstract class ILevelRepository { abstract class ILevelRepository {
Future<DataState<LevelEntity, MyException>> getData({required LevelParams params});
Future<DataState<List<LevelEntity>, MyException>> getLevels({required LevelParams params});
} }

6
lib/features/level/domain/usecases/get_level_usecase.dart

@ -5,13 +5,13 @@ 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/level/domain/entity/level_entity.dart';
import 'package:hadi_hoda_flutter/features/level/domain/repository/level_repository.dart'; import 'package:hadi_hoda_flutter/features/level/domain/repository/level_repository.dart';
class GetLevelUseCase implements UseCase<LevelEntity, LevelParams> {
class GetLevelUseCase implements UseCase<List<LevelEntity>, LevelParams> {
final ILevelRepository repository; final ILevelRepository repository;
const GetLevelUseCase(this.repository); const GetLevelUseCase(this.repository);
@override @override
Future<DataState<LevelEntity, MyException>> call(LevelParams params) {
return repository.getData(params: params);
Future<DataState<List<LevelEntity>, MyException>> call(LevelParams params) {
return repository.getLevels(params: params);
} }
} }

69
lib/features/level/presentation/bloc/level_bloc.dart

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:hadi_hoda_flutter/core/params/level_params.dart';
import 'package:hadi_hoda_flutter/core/status/base_status.dart'; 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/level/domain/entity/level_entity.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_location.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/level_location.dart';
@ -12,56 +13,60 @@ class LevelBloc extends Bloc<LevelEvent, LevelState> {
LevelBloc( LevelBloc(
this._getLevelUseCase, this._getLevelUseCase,
) : super(const LevelState()) { ) : super(const LevelState()) {
on<GetLevelEvent>(_getLevelEvent);
on<GetLevelListEvent>(_getLevelListEvent);
} }
/// ------------UseCases------------ /// ------------UseCases------------
final GetLevelUseCase _getLevelUseCase; final GetLevelUseCase _getLevelUseCase;
/// ------------Variables------------ /// ------------Variables------------
final List<MissionLocation> bottomLocationList = [
MissionLocation(bottom: -30, left: 30, index: 1),
MissionLocation(bottom: 50, left: 100, index: 2),
MissionLocation(bottom: 150, left: 60, index: 3),
MissionLocation(bottom: 210, left: 120, index: 4),
MissionLocation(bottom: 250, right: 60, index: 5),
MissionLocation(top: 170, right: 40, index: 6),
MissionLocation(top: 70, right: 70, index: 7),
MissionLocation(top: -20, right: 70, index: 8),
final List<LevelLocation> bottomLocationList = [
LevelLocation(bottom: -30, left: 30, index: 1),
LevelLocation(bottom: 50, left: 100, index: 2),
LevelLocation(bottom: 150, left: 60, index: 3),
LevelLocation(bottom: 210, left: 120, index: 4),
LevelLocation(bottom: 250, right: 60, index: 5),
LevelLocation(top: 170, right: 40, index: 6),
LevelLocation(top: 70, right: 70, index: 7),
LevelLocation(top: -20, right: 70, index: 8),
]; ];
final List<MissionLocation> topLocationList = [
MissionLocation(bottom: 30, right: 80, index: 9),
MissionLocation(bottom: 70, left: 20, index: 10),
MissionLocation(bottom: 150, left: 50, index: 11),
MissionLocation(bottom: 180, left: 140, index: 12),
MissionLocation(bottom: 260, right: 20, index: 13),
MissionLocation(bottom: 370, right: 30, index: 14),
MissionLocation(bottom: 420, left: 40, index: 15),
MissionLocation(top: 410, left: 0, index: 16),
MissionLocation(top: 320, left: 60, index: 17),
MissionLocation(top: 220, left: 80, index: 18),
MissionLocation(top: 130, left: 20, index: 19),
MissionLocation(top: 50, left: 70, index: 20),
final List<LevelLocation> topLocationList = [
LevelLocation(bottom: 30, right: 80, index: 9),
LevelLocation(bottom: 70, left: 20, index: 10),
LevelLocation(bottom: 150, left: 50, index: 11),
LevelLocation(bottom: 180, left: 140, index: 12),
LevelLocation(bottom: 260, right: 20, index: 13),
LevelLocation(bottom: 370, right: 30, index: 14),
LevelLocation(bottom: 420, left: 40, index: 15),
LevelLocation(top: 410, left: 0, index: 16),
LevelLocation(top: 320, left: 60, index: 17),
LevelLocation(top: 220, left: 80, index: 18),
LevelLocation(top: 130, left: 20, index: 19),
LevelLocation(top: 50, left: 70, index: 20),
]; ];
final List<LevelEntity> bottom8LevelList = [];
final List<LevelEntity> top12LevelList = [];
/// ------------Controllers------------ /// ------------Controllers------------
/// ------------Functions------------ /// ------------Functions------------
/// ------------Api Calls------------ /// ------------Api Calls------------
FutureOr<void> _getLevelEvent(event, emit) async {
await _getLevelUseCase(event.levelParams).then(
(value) {
FutureOr<void> _getLevelListEvent(GetLevelListEvent event,
Emitter<LevelState> emit) async {
await _getLevelUseCase(LevelParams()).then((value) {
value.fold( value.fold(
(data) { (data) {
emit(state.copyWith(getLevelStatus: BaseComplete<LevelEntity>(data)));
},
(error) {
emit(state.copyWith(getLevelStatus: BaseError(error.errorMessage)));
},
);
bottom8LevelList.addAll(data.take(8));
if(data.length > 8){
top12LevelList.addAll(data.sublist(8, data.length));
}
emit(state.copyWith(getLevelStatus: const BaseComplete('')));
}, },
(error) {},
); );
});
} }
} }

2
lib/features/level/presentation/bloc/level_event.dart

@ -2,4 +2,4 @@ sealed class LevelEvent {
const LevelEvent(); const LevelEvent();
} }
class GetLevelEvent extends LevelEvent {}
class GetLevelListEvent extends LevelEvent {}

13
lib/features/level/presentation/ui/level_page.dart

@ -5,6 +5,7 @@ import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart';
import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/utils/my_image.dart';
import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; import 'package:hadi_hoda_flutter/core/utils/screen_size.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_bloc.dart'; import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_bloc.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/bloc/level_state.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/bottom_path.dart'; import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/bottom_path.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/hint_level_widget.dart'; import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/hint_level_widget.dart';
import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/level_widget.dart'; import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/level_widget.dart';
@ -71,14 +72,15 @@ class LevelPage extends StatelessWidget {
return Positioned( return Positioned(
top: context.heightScreen * 0.16, top: context.heightScreen * 0.16,
left: context.widthScreen * 0.15, left: context.widthScreen * 0.15,
child: Stack(
child: BlocBuilder<LevelBloc, LevelState>(
builder: (context, state) => Stack(
children: [ children: [
TopPath( TopPath(
height: 950, height: 950,
width: context.widthScreen * 0.6, width: context.widthScreen * 0.6,
), ),
...List.generate( ...List.generate(
context.read<LevelBloc>().topLocationList.length,
context.read<LevelBloc>().top12LevelList.length,
(index) => Positioned( (index) => Positioned(
top: context.read<LevelBloc>().topLocationList[index].top, top: context.read<LevelBloc>().topLocationList[index].top,
bottom: context.read<LevelBloc>().topLocationList[index].bottom, bottom: context.read<LevelBloc>().topLocationList[index].bottom,
@ -91,6 +93,7 @@ class LevelPage extends StatelessWidget {
), ),
], ],
), ),
),
); );
} }
@ -98,7 +101,8 @@ class LevelPage extends StatelessWidget {
return Positioned( return Positioned(
bottom: context.heightScreen * 0.18, bottom: context.heightScreen * 0.18,
left: context.widthScreen * 0.2, left: context.widthScreen * 0.2,
child: Stack(
child: BlocBuilder<LevelBloc, LevelState>(
builder: (context, state) => Stack(
clipBehavior: Clip.none, clipBehavior: Clip.none,
children: [ children: [
BottomPath( BottomPath(
@ -106,7 +110,7 @@ class LevelPage extends StatelessWidget {
height: context.heightScreen * 0.6, height: context.heightScreen * 0.6,
), ),
...List.generate( ...List.generate(
context.read<LevelBloc>().bottomLocationList.length,
context.read<LevelBloc>().bottom8LevelList.length,
(index) => Positioned( (index) => Positioned(
top: context.read<LevelBloc>().bottomLocationList[index].top, top: context.read<LevelBloc>().bottomLocationList[index].top,
bottom: context.read<LevelBloc>().bottomLocationList[index].bottom, bottom: context.read<LevelBloc>().bottomLocationList[index].bottom,
@ -119,6 +123,7 @@ class LevelPage extends StatelessWidget {
), ),
], ],
), ),
),
); );
} }
} }

2
lib/features/level/presentation/ui/widgets/level_widget.dart

@ -31,7 +31,7 @@ class LevelWidget extends StatelessWidget {
MyImage(image: LevelType.image[type] ?? MyAssets.level, size: 46), MyImage(image: LevelType.image[type] ?? MyAssets.level, size: 46),
Text( Text(
'$index', '$index',
style: MyTextStyle.normal.copyWith(color: context.primaryColor),
style: MyTextStyle.normal26.copyWith(color: context.primaryColor),
), ),
if(type == LevelType.current) if(type == LevelType.current)
Positioned( Positioned(

26
lib/features/question/data/model/answer_model.dart

@ -0,0 +1,26 @@
import 'package:hadi_hoda_flutter/features/question/data/model/file_model.dart';
import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart';
class AnswerModel extends AnswerEntity {
AnswerModel({
super.id,
super.title,
super.imageId,
super.imageInfo,
super.order,
super.isActive,
});
factory AnswerModel.fromJson(Map<String, dynamic> json) {
return AnswerModel(
id: json['id'],
title: json['title'],
imageId: json['image_id'],
imageInfo: json['image_info'] == null
? null
: FileModel.fromJson(json['image_info']),
order: json['order'],
isActive: json['is_active'],
);
}
}

17
lib/features/question/data/model/file_model.dart

@ -0,0 +1,17 @@
import 'package:hadi_hoda_flutter/features/question/domain/entity/file_entity.dart';
class FileModel extends FileEntity {
FileModel({
super.filename,
super.size,
super.extension,
});
factory FileModel.fromJson(Map<String, dynamic> json) {
return FileModel(
filename: json['filename'],
size: json['size'],
extension: json['extension'],
);
}
}

23
lib/features/question/data/model/question_model.dart

@ -1,13 +1,34 @@
import 'package:hadi_hoda_flutter/features/question/data/model/answer_model.dart';
import 'package:hadi_hoda_flutter/features/question/data/model/file_model.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'; import 'package:hadi_hoda_flutter/features/question/domain/entity/question_entity.dart';
class QuestionModel extends QuestionEntity { class QuestionModel extends QuestionEntity {
const QuestionModel({
QuestionModel({
super.id, super.id,
super.title,
super.audioId,
super.audioInfo,
super.order,
super.correctAnswer,
super.isActive,
super.answers,
}); });
factory QuestionModel.fromJson(Map<String, dynamic> json) { factory QuestionModel.fromJson(Map<String, dynamic> json) {
return QuestionModel( return QuestionModel(
id: json['id'], id: json['id'],
title: json['title'],
audioId: json['audio_id'],
audioInfo: json['audio_info'] == null
? null
: FileModel.fromJson(json['audio_info']),
order: json['order'],
correctAnswer: json['correct_answer'],
isActive: json['is_active'],
answers: json['answers']
?.map<AnswerEntity>((e) => AnswerModel.fromJson(e))
.toList(),
); );
} }
} }

29
lib/features/question/domain/entity/answer_entity.dart

@ -0,0 +1,29 @@
import 'package:hadi_hoda_flutter/features/question/domain/entity/file_entity.dart';
import 'package:hive/hive.dart';
part 'answer_entity.g.dart';
@HiveType(typeId: 2)
class AnswerEntity extends HiveObject {
@HiveField(0)
int? id;
@HiveField(1)
String? title;
@HiveField(2)
String? imageId;
@HiveField(3)
FileEntity? imageInfo;
@HiveField(4)
int? order;
@HiveField(5)
bool? isActive;
AnswerEntity({
this.id,
this.title,
this.imageId,
this.imageInfo,
this.order,
this.isActive,
});
}

56
lib/features/question/domain/entity/answer_entity.g.dart

@ -0,0 +1,56 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'answer_entity.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class AnswerEntityAdapter extends TypeAdapter<AnswerEntity> {
@override
final int typeId = 2;
@override
AnswerEntity read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return AnswerEntity(
id: fields[0] as int?,
title: fields[1] as String?,
imageId: fields[2] as String?,
imageInfo: fields[3] as FileEntity?,
order: fields[4] as int?,
isActive: fields[5] as bool?,
);
}
@override
void write(BinaryWriter writer, AnswerEntity obj) {
writer
..writeByte(6)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.title)
..writeByte(2)
..write(obj.imageId)
..writeByte(3)
..write(obj.imageInfo)
..writeByte(4)
..write(obj.order)
..writeByte(5)
..write(obj.isActive);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AnswerEntityAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

19
lib/features/question/domain/entity/file_entity.dart

@ -0,0 +1,19 @@
import 'package:hive/hive.dart';
part 'file_entity.g.dart';
@HiveType(typeId: 3)
class FileEntity extends HiveObject {
@HiveField(0)
String? filename;
@HiveField(1)
int? size;
@HiveField(2)
String? extension;
FileEntity({
this.filename,
this.size,
this.extension,
});
}

47
lib/features/question/domain/entity/file_entity.g.dart

@ -0,0 +1,47 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'file_entity.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class FileEntityAdapter extends TypeAdapter<FileEntity> {
@override
final int typeId = 3;
@override
FileEntity read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return FileEntity(
filename: fields[0] as String?,
size: fields[1] as int?,
extension: fields[2] as String?,
);
}
@override
void write(BinaryWriter writer, FileEntity obj) {
writer
..writeByte(3)
..writeByte(0)
..write(obj.filename)
..writeByte(1)
..write(obj.size)
..writeByte(2)
..write(obj.extension);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is FileEntityAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

40
lib/features/question/domain/entity/question_entity.dart

@ -1,14 +1,36 @@
import 'package:equatable/equatable.dart';
import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart';
import 'package:hadi_hoda_flutter/features/question/domain/entity/file_entity.dart';
import 'package:hive/hive.dart';
class QuestionEntity extends Equatable {
final int? id;
part 'question_entity.g.dart';
const QuestionEntity({
@HiveType(typeId: 1)
class QuestionEntity extends HiveObject {
@HiveField(0)
int? id;
@HiveField(1)
String? title;
@HiveField(2)
String? audioId;
@HiveField(3)
FileEntity? audioInfo;
@HiveField(4)
int? order;
@HiveField(5)
int? correctAnswer;
@HiveField(6)
bool? isActive;
@HiveField(7)
List<AnswerEntity>? answers;
QuestionEntity({
this.id, this.id,
this.title,
this.audioId,
this.audioInfo,
this.order,
this.correctAnswer,
this.isActive,
this.answers,
}); });
@override
List<Object?> get props => [
id,
];
} }

62
lib/features/question/domain/entity/question_entity.g.dart

@ -0,0 +1,62 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'question_entity.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class QuestionEntityAdapter extends TypeAdapter<QuestionEntity> {
@override
final int typeId = 1;
@override
QuestionEntity read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return QuestionEntity(
id: fields[0] as int?,
title: fields[1] as String?,
audioId: fields[2] as String?,
audioInfo: fields[3] as FileEntity?,
order: fields[4] as int?,
correctAnswer: fields[5] as int?,
isActive: fields[6] as bool?,
answers: (fields[7] as List?)?.cast<AnswerEntity>(),
);
}
@override
void write(BinaryWriter writer, QuestionEntity obj) {
writer
..writeByte(8)
..writeByte(0)
..write(obj.id)
..writeByte(1)
..write(obj.title)
..writeByte(2)
..write(obj.audioId)
..writeByte(3)
..write(obj.audioInfo)
..writeByte(4)
..write(obj.order)
..writeByte(5)
..write(obj.correctAnswer)
..writeByte(6)
..write(obj.isActive)
..writeByte(7)
..write(obj.answers);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is QuestionEntityAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

40
lib/init_bindings.dart

@ -1,15 +1,28 @@
import 'dart:io';
import 'package:hadi_hoda_flutter/core/constants/my_constants.dart';
import 'package:hadi_hoda_flutter/core/network/http_request.dart'; import 'package:hadi_hoda_flutter/core/network/http_request.dart';
import 'package:hadi_hoda_flutter/core/network/http_request_impl.dart'; import 'package:hadi_hoda_flutter/core/network/http_request_impl.dart';
import 'package:hadi_hoda_flutter/features/home/data/datasource/home_datasource.dart';
import 'package:hadi_hoda_flutter/features/home/data/repository_impl/home_repository_impl.dart';
import 'package:hadi_hoda_flutter/features/home/domain/repository/home_repository.dart';
import 'package:hadi_hoda_flutter/features/home/domain/usecases/get_home_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/data/datasource/intro_datasource.dart'; import 'package:hadi_hoda_flutter/features/intro/data/datasource/intro_datasource.dart';
import 'package:hadi_hoda_flutter/features/intro/data/repository_impl/intro_repository_impl.dart'; 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/repository/intro_repository.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/get_intro_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/extract_data_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/loading_stream_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/save_levels_usecase.dart';
import 'package:hadi_hoda_flutter/features/level/data/datasource/level_datasource.dart'; import 'package:hadi_hoda_flutter/features/level/data/datasource/level_datasource.dart';
import 'package:hadi_hoda_flutter/features/level/data/repository_impl/level_repository_impl.dart'; import 'package:hadi_hoda_flutter/features/level/data/repository_impl/level_repository_impl.dart';
import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart';
import 'package:hadi_hoda_flutter/features/level/domain/repository/level_repository.dart'; import 'package:hadi_hoda_flutter/features/level/domain/repository/level_repository.dart';
import 'package:hadi_hoda_flutter/features/level/domain/usecases/get_level_usecase.dart'; import 'package:hadi_hoda_flutter/features/level/domain/usecases/get_level_usecase.dart';
import 'package:hadi_hoda_flutter/features/question/data/datasource/question_datasource.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/data/repository_impl/question_repository_impl.dart';
import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart';
import 'package:hadi_hoda_flutter/features/question/domain/entity/file_entity.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'; 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/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/datasource/sample_datasource.dart';
@ -17,6 +30,8 @@ import 'package:hadi_hoda_flutter/features/sample/data/repository_impl/sample_re
import 'package:hadi_hoda_flutter/features/sample/domain/repository/sample_repository.dart'; import 'package:hadi_hoda_flutter/features/sample/domain/repository/sample_repository.dart';
import 'package:hadi_hoda_flutter/features/sample/domain/usecases/get_sample_usecase.dart'; import 'package:hadi_hoda_flutter/features/sample/domain/usecases/get_sample_usecase.dart';
import 'package:get_it/get_it.dart'; import 'package:get_it/get_it.dart';
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart';
final GetIt locator = GetIt.I; final GetIt locator = GetIt.I;
@ -32,7 +47,14 @@ void initBindings() {
/// Intro Feature /// Intro Feature
locator.registerLazySingleton<IIntroDatasource>(() => IntroDatasourceImpl(locator())); locator.registerLazySingleton<IIntroDatasource>(() => IntroDatasourceImpl(locator()));
locator.registerLazySingleton<IIntroRepository>(() => IntroRepositoryImpl(locator())); locator.registerLazySingleton<IIntroRepository>(() => IntroRepositoryImpl(locator()));
locator.registerLazySingleton<GetIntroUseCase>(() => GetIntroUseCase(locator()));
locator.registerLazySingleton<SaveLevelsUseCase>(() => SaveLevelsUseCase(locator()));
locator.registerLazySingleton<ExtractDataUseCase>(() => ExtractDataUseCase(locator()));
locator.registerLazySingleton<LoadingStreamUseCase>(() => LoadingStreamUseCase(locator()));
/// Home Feature
locator.registerLazySingleton<IHomeDatasource>(() => HomeDatasourceImpl(locator()));
locator.registerLazySingleton<IHomeRepository>(() => HomeRepositoryImpl(locator()));
locator.registerLazySingleton<GetHomeUseCase>(() => GetHomeUseCase(locator()));
/// Question Feature /// Question Feature
locator.registerLazySingleton<IQuestionDatasource>(() => QuestionDatasourceImpl(locator())); locator.registerLazySingleton<IQuestionDatasource>(() => QuestionDatasourceImpl(locator()));
@ -40,7 +62,19 @@ void initBindings() {
locator.registerLazySingleton<GetQuestionUseCase>(() => GetQuestionUseCase(locator())); locator.registerLazySingleton<GetQuestionUseCase>(() => GetQuestionUseCase(locator()));
/// Level Feature /// Level Feature
locator.registerLazySingleton<ILevelDatasource>(() => LevelDatasourceImpl(locator()));
locator.registerLazySingleton<ILevelDatasource>(() => LocalLevelDatasourceImpl());
locator.registerLazySingleton<ILevelRepository>(() => LevelRepositoryImpl(locator())); locator.registerLazySingleton<ILevelRepository>(() => LevelRepositoryImpl(locator()));
locator.registerLazySingleton<GetLevelUseCase>(() => GetLevelUseCase(locator())); locator.registerLazySingleton<GetLevelUseCase>(() => GetLevelUseCase(locator()));
} }
Future<void> initDataBase() async {
final Directory dir = await getApplicationDocumentsDirectory();
Hive
..init(dir.path)
..registerAdapter<FileEntity>(FileEntityAdapter())
..registerAdapter<AnswerEntity>(AnswerEntityAdapter())
..registerAdapter<QuestionEntity>(QuestionEntityAdapter())
..registerAdapter<LevelEntity>(LevelEntityAdapter());
await Hive.openBox<LevelEntity>(MyConstants.levelBox);
}

3
lib/main.dart

@ -7,13 +7,13 @@ import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:hadi_hoda_flutter/common_ui/theme/my_theme.dart'; import 'package:hadi_hoda_flutter/common_ui/theme/my_theme.dart';
import 'package:hadi_hoda_flutter/core/auth_storage/auth_storage.dart'; import 'package:hadi_hoda_flutter/core/auth_storage/auth_storage.dart';
import 'package:hadi_hoda_flutter/core/routers/my_routes.dart'; import 'package:hadi_hoda_flutter/core/routers/my_routes.dart';
import 'package:hadi_hoda_flutter/core/utils/my_scroll_behavior.dart';
Future<void> main() async { Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
initBindings(); initBindings();
await Future.wait([ await Future.wait([
LocalStorage.init(), LocalStorage.init(),
initDataBase(),
]); ]);
AuthStorage.loadData(); AuthStorage.loadData();
runApp(const MainApp()); runApp(const MainApp());
@ -30,7 +30,6 @@ class MainApp extends StatelessWidget {
themeMode: ThemeService.getTheme(), themeMode: ThemeService.getTheme(),
locale: const Locale('en', 'US'), locale: const Locale('en', 'US'),
supportedLocales: const [Locale('en', 'US')], supportedLocales: const [Locale('en', 'US')],
scrollBehavior: MyScrollBehavior(),
routerConfig: appPages, routerConfig: appPages,
localizationsDelegates: const [ localizationsDelegates: const [
AppLocalizations.delegate, AppLocalizations.delegate,

314
pubspec.lock

@ -1,6 +1,22 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7"
url: "https://pub.dev"
source: hosted
version: "67.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d"
url: "https://pub.dev"
source: hosted
version: "6.4.1"
archive: archive:
dependency: transitive dependency: transitive
description: description:
@ -41,6 +57,70 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.2"
build:
dependency: transitive
description:
name: build
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
build_config:
dependency: transitive
description:
name: build_config
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa"
url: "https://pub.dev"
source: hosted
version: "4.0.4"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d"
url: "https://pub.dev"
source: hosted
version: "2.4.13"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0
url: "https://pub.dev"
source: hosted
version: "7.3.2"
built_collection:
dependency: transitive
description:
name: built_collection
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d
url: "https://pub.dev"
source: hosted
version: "8.12.0"
characters: characters:
dependency: transitive dependency: transitive
description: description:
@ -49,6 +129,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.4.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
url: "https://pub.dev"
source: hosted
version: "2.0.4"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@ -57,6 +145,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.2" version: "1.1.2"
code_builder:
dependency: transitive
description:
name: code_builder
sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243"
url: "https://pub.dev"
source: hosted
version: "4.11.0"
collection: collection:
dependency: transitive dependency: transitive
description: description:
@ -65,6 +161,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.19.1" version: "1.19.1"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.2"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -73,6 +177,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.6" version: "3.0.6"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9"
url: "https://pub.dev"
source: hosted
version: "2.3.6"
dio: dio:
dependency: "direct main" dependency: "direct main"
description: description:
@ -129,11 +241,27 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "7.0.1" version: "7.0.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
url: "https://pub.dev"
source: hosted
version: "1.1.1"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_archive:
dependency: "direct main"
description:
name: flutter_archive
sha256: "5ca235f304c12bf468979235f400f79846d204169d715939e39197106f5fc970"
url: "https://pub.dev"
source: hosted
version: "6.0.3"
flutter_bloc: flutter_bloc:
dependency: "direct main" dependency: "direct main"
description: description:
@ -173,6 +301,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.dev"
source: hosted
version: "4.0.0"
get_it: get_it:
dependency: "direct main" dependency: "direct main"
description: description:
@ -181,6 +317,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.2.0" version: "8.2.0"
glob:
dependency: transitive
description:
name: glob
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
url: "https://pub.dev"
source: hosted
version: "2.1.3"
go_router: go_router:
dependency: "direct main" dependency: "direct main"
description: description:
@ -197,6 +341,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.3.2" version: "6.3.2"
graphs:
dependency: transitive
description:
name: graphs
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
hive:
dependency: "direct main"
description:
name: hive
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
url: "https://pub.dev"
source: hosted
version: "2.2.3"
hive_generator:
dependency: "direct dev"
description:
name: hive_generator
sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
http: http:
dependency: transitive dependency: transitive
description: description:
@ -205,6 +373,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.0" version: "1.5.0"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
url: "https://pub.dev"
source: hosted
version: "3.2.2"
http_parser: http_parser:
dependency: transitive dependency: transitive
description: description:
@ -221,6 +397,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.20.2" version: "0.20.2"
io:
dependency: transitive
description:
name: io
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
url: "https://pub.dev"
source: hosted
version: "1.0.5"
js:
dependency: transitive
description:
name: js
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
url: "https://pub.dev"
source: hosted
version: "0.7.2"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
leak_tracker: leak_tracker:
dependency: transitive dependency: transitive
description: description:
@ -309,6 +509,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.0" version: "1.0.0"
package_config:
dependency: transitive
description:
name: package_config
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
url: "https://pub.dev"
source: hosted
version: "2.2.0"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -334,7 +542,7 @@ packages:
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
path_provider: path_provider:
dependency: transitive
dependency: "direct main"
description: description:
name: path_provider name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
@ -405,6 +613,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.8" version: "2.1.8"
pool:
dependency: transitive
description:
name: pool
sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d"
url: "https://pub.dev"
source: hosted
version: "1.5.2"
posix: posix:
dependency: transitive dependency: transitive
description: description:
@ -429,6 +645,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.5+1" version: "6.1.5+1"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
shared_preferences: shared_preferences:
dependency: "direct main" dependency: "direct main"
description: description:
@ -485,6 +717,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.1" version: "2.4.1"
shelf:
dependency: transitive
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.2"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
url: "https://pub.dev"
source: hosted
version: "2.0.1"
showcaseview: showcaseview:
dependency: "direct main" dependency: "direct main"
description: description:
@ -498,6 +746,22 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
url: "https://pub.dev"
source: hosted
version: "1.3.5"
source_span: source_span:
dependency: transitive dependency: transitive
description: description:
@ -522,6 +786,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.4"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
url: "https://pub.dev"
source: hosted
version: "2.1.1"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
@ -546,6 +818,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.6" version: "0.7.6"
timing:
dependency: transitive
description:
name: timing
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -594,6 +874,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "15.0.2" version: "15.0.2"
watcher:
dependency: transitive
description:
name: watcher
sha256: "5bf046f41320ac97a469d506261797f35254fa61c641741ef32dacda98b7d39c"
url: "https://pub.dev"
source: hosted
version: "1.1.3"
web: web:
dependency: transitive dependency: transitive
description: description:
@ -602,6 +890,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.1" version: "1.1.1"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
url: "https://pub.dev"
source: hosted
version: "3.0.3"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -618,6 +922,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.6.1" version: "6.6.1"
yaml:
dependency: transitive
description:
name: yaml
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.dev"
source: hosted
version: "3.1.3"
sdks: sdks:
dart: ">=3.9.2 <4.0.0" dart: ">=3.9.2 <4.0.0"
flutter: ">=3.35.0" flutter: ">=3.35.0"

10
pubspec.yaml

@ -13,6 +13,7 @@ dependencies:
equatable: ^2.0.7 equatable: ^2.0.7
flutter: flutter:
sdk: flutter sdk: flutter
flutter_archive: ^6.0.3
flutter_bloc: ^9.1.1 flutter_bloc: ^9.1.1
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
@ -20,8 +21,10 @@ dependencies:
get_it: ^8.2.0 get_it: ^8.2.0
go_router: ^16.1.0 go_router: ^16.1.0
google_fonts: ^6.3.2 google_fonts: ^6.3.2
hive: ^2.2.3
intl: ^0.20.2 intl: ^0.20.2
path_drawing: ^1.0.1 path_drawing: ^1.0.1
path_provider: ^2.1.5
pretty_dio_logger: ^1.4.0 pretty_dio_logger: ^1.4.0
shared_preferences: ^2.5.3 shared_preferences: ^2.5.3
showcaseview: ^4.0.1 showcaseview: ^4.0.1
@ -30,14 +33,19 @@ dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^5.0.0 flutter_lints: ^5.0.0
hive_generator: ^2.0.1
build_runner: ^2.4.13
flutter: flutter:
uses-material-design: true uses-material-design: true
generate: true generate: true
assets: assets:
- assets/images/
- assets/audio/
- assets/data/
- assets/fonts/ - assets/fonts/
- assets/images/
- assets/json/
fonts: fonts:

Loading…
Cancel
Save