Browse Source

Merge pull request 'feature/intro' (#11) from feature/intro into develop

Reviewed-on: https://git.nwhco.ir/amirreza.chegini/hade_hoda_flutter/pulls/11
pull/13/head
amirreza.chegini 5 days ago
parent
commit
505c72d89b
  1. BIN
      assets/fonts/Marhey-Bold.ttf
  2. BIN
      assets/fonts/Marhey-Light.ttf
  3. BIN
      assets/fonts/Marhey-Medium.ttf
  4. BIN
      assets/fonts/Marhey-Regular.ttf
  5. BIN
      assets/fonts/Marhey-SemiBold.ttf
  6. 1181
      assets/json/levels.json
  7. 70
      lib/common_ui/resources/my_text_style.dart
  8. 6
      lib/core/constants/my_api.dart
  9. 2
      lib/core/constants/my_constants.dart
  10. 2
      lib/core/error_handler/error_handler.dart
  11. 2
      lib/core/routers/my_routes.dart
  12. 2
      lib/core/utils/local_storage.dart
  13. 6
      lib/core/utils/storage_path.dart
  14. 18
      lib/core/widgets/about_us_dialog/about_us_dialog.dart
  15. 3
      lib/core/widgets/answer_box/answer_box.dart
  16. 8
      lib/core/widgets/answer_box/styles/picture_box.dart
  17. 8
      lib/core/widgets/answer_box/styles/text_box.dart
  18. 6
      lib/core/widgets/showcase/question_showcase.dart
  19. 86
      lib/features/intro/data/datasource/intro_datasource.dart
  20. 20
      lib/features/intro/data/repository_impl/intro_repository_impl.dart
  21. 3
      lib/features/intro/domain/repository/intro_repository.dart
  22. 16
      lib/features/intro/domain/usecases/extract_data_usecase.dart
  23. 6
      lib/features/intro/domain/usecases/get_files_usecase.dart
  24. 49
      lib/features/intro/presentation/bloc/intro_bloc.dart
  25. 4
      lib/features/intro/presentation/bloc/intro_event.dart
  26. 8
      lib/features/intro/presentation/bloc/intro_state.dart
  27. 23
      lib/features/intro/presentation/ui/intro_page.dart
  28. 4
      lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart
  29. 1
      lib/features/level/presentation/ui/level_page.dart
  30. 10
      lib/features/level/presentation/ui/widgets/hint_level_widget.dart
  31. 11
      lib/features/level/presentation/ui/widgets/level_widget.dart
  32. 1
      lib/features/question/presentation/bloc/question_bloc.dart
  33. 21
      lib/features/question/presentation/ui/question_page.dart
  34. 6
      lib/features/question/presentation/ui/widgets/left_blob.dart
  35. 6
      lib/features/question/presentation/ui/widgets/right_blob.dart
  36. 6
      lib/init_bindings.dart
  37. 2
      lib/main.dart
  38. 8
      pubspec.lock
  39. 24
      pubspec.yaml

BIN
assets/fonts/Marhey-Bold.ttf

BIN
assets/fonts/Marhey-Light.ttf

BIN
assets/fonts/Marhey-Medium.ttf

BIN
assets/fonts/Marhey-Regular.ttf

BIN
assets/fonts/Marhey-SemiBold.ttf

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

70
lib/common_ui/resources/my_text_style.dart

@ -1,28 +1,70 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class MyTextStyle {
static const MyTextStyle _i = MyTextStyle._internal();
const MyTextStyle._internal();
factory MyTextStyle() => _i;
class DinoKids {
static const DinoKids _i = DinoKids._internal();
const DinoKids._internal();
factory DinoKids() => _i;
static const String fontFamily = 'dinokids'; static const String fontFamily = 'dinokids';
static const TextStyle normal26 = TextStyle(
/// Regular
static const TextStyle regular17 = TextStyle(
fontFamily: fontFamily,
fontSize: 17,
fontWeight: FontWeight.w400,
);
static const TextStyle regular26 = TextStyle(
fontFamily: fontFamily, fontFamily: fontFamily,
fontSize: 26, fontSize: 26,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
shadows: [
Shadow(
color: Color(0XFF5B5B5B),
blurRadius: 2.86,
offset: Offset(0, 2),
),
]
);
}
class Marhey {
static const Marhey _i = Marhey._internal();
const Marhey._internal();
factory Marhey() => _i;
static const String fontFamily = 'marhey';
/// Medium
static const TextStyle medium12 = TextStyle(
fontFamily: fontFamily,
fontSize: 12,
fontWeight: FontWeight.w500,
);
static const TextStyle medium16 = TextStyle(
fontFamily: fontFamily,
fontSize: 16,
fontWeight: FontWeight.w500,
); );
static const TextStyle normal17 = TextStyle(
/// Semi Bold
static const TextStyle semiBold17 = TextStyle(
fontFamily: fontFamily, fontFamily: fontFamily,
fontSize: 17, fontSize: 17,
fontWeight: FontWeight.w400,
fontWeight: FontWeight.w600,
);
static const TextStyle semiBold18 = TextStyle(
fontFamily: fontFamily,
fontSize: 18,
fontWeight: FontWeight.w600,
);
static const TextStyle semiBold22 = TextStyle(
fontFamily: fontFamily,
fontSize: 22,
fontWeight: FontWeight.w600,
);
/// Bold
static const TextStyle bold12 = TextStyle(
fontFamily: fontFamily,
fontSize: 12,
fontWeight: FontWeight.w700,
);
static const TextStyle bold14 = TextStyle(
fontFamily: fontFamily,
fontSize: 14,
fontWeight: FontWeight.w700,
); );
} }

6
lib/core/constants/my_api.dart

@ -6,6 +6,10 @@ class MyApi {
static const Duration timeOut = Duration(seconds: 30); static const Duration timeOut = Duration(seconds: 30);
static const String contentType = 'application/json'; static const String contentType = 'application/json';
static const String defaultError = 'An unexpected error has occurred.'; static const String defaultError = 'An unexpected error has occurred.';
static const String noInternetError = 'Please check your internet connection.';
static const String baseUrl = 'https://api.BASE_URL.com';
static const String baseUrl = 'https://hadihoda.newhorizonco.uk/api';
static const String levels = '/quiz/optimized/levels/';
static const String files = '/quiz/optimized/download-all-files/';
} }

2
lib/core/constants/my_constants.dart

@ -6,4 +6,6 @@ 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'; static const String levelBox = 'LEVEL_BOX';
static const String downloadCompleted = 'DOWNLOAD_COMPLETED';
static const String extractCompleted = 'EXTRACT_COMPLETED';
} }

2
lib/core/error_handler/error_handler.dart

@ -10,7 +10,7 @@ class ErrorHandler {
static void handleError(DioException e) { static void handleError(DioException e) {
if (e.response == null) { if (e.response == null) {
throw MyException( throw MyException(
errorMessage: e.message ?? MyApi.defaultError,
errorMessage: MyApi.noInternetError,
statusCode: e.response?.statusCode, statusCode: e.response?.statusCode,
); );
} else { } else {

2
lib/core/routers/my_routes.dart

@ -32,7 +32,7 @@ GoRouter get appPages => GoRouter(
name: Routes.introPage, name: Routes.introPage,
path: Routes.introPage, path: Routes.introPage,
builder: (context, state) => BlocProvider( builder: (context, state) => BlocProvider(
create: (context) => IntroBloc(locator(), locator(), locator()),
create: (context) => IntroBloc(locator(), locator()),
child: const IntroPage(), child: const IntroPage(),
), ),
), ),

2
lib/core/utils/local_storage.dart

@ -11,7 +11,7 @@ class LocalStorage {
_box = await SharedPreferences.getInstance(); _box = await SharedPreferences.getInstance();
} }
static Future<void> saveData({required String key, required dynamic value}) async {
static Future<void> saveData({required String key, required String value}) async {
await _box.setString(key, value); await _box.setString(key, value);
} }

6
lib/core/utils/storage_path.dart

@ -3,9 +3,9 @@ import 'dart:io';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
class StoragePath { class StoragePath {
static Directory applicationDir = Directory('');
static Directory documentDir = Directory('');
static Future<void> getApplicationDir() async {
applicationDir = await getApplicationDocumentsDirectory();
static Future<void> getDocumentDir() async {
documentDir = await getApplicationDocumentsDirectory();
} }
} }

18
lib/core/widgets/about_us_dialog/about_us_dialog.dart

@ -2,8 +2,8 @@ import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.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/utils/check_platform.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';
@ -46,26 +46,14 @@ class AboutUsDialog extends StatelessWidget {
children: [ children: [
Text( Text(
context.translate.about_us, context.translate.about_us,
style: GoogleFonts.marhey(
style: Marhey.semiBold22.copyWith(
color: Color(0XFF322386), color: Color(0XFF322386),
fontSize: checkSize(
context: context,
mobile: 22,
tablet: 30,
),
fontWeight: FontWeight.w600,
), ),
), ),
Text( Text(
context.translate.about_us_desc, context.translate.about_us_desc,
style: GoogleFonts.marhey(
style: Marhey.medium16.copyWith(
color: Color(0XFF494178), color: Color(0XFF494178),
fontSize: checkSize(
context: context,
mobile: 16,
tablet: 21,
),
fontWeight: FontWeight.w500,
), ),
), ),
MyImage( MyImage(

3
lib/core/widgets/answer_box/answer_box.dart

@ -3,7 +3,6 @@ import 'package:hadi_hoda_flutter/core/utils/storage_path.dart';
import 'package:hadi_hoda_flutter/core/widgets/answer_box/styles/picture_box.dart'; import 'package:hadi_hoda_flutter/core/widgets/answer_box/styles/picture_box.dart';
import 'package:hadi_hoda_flutter/core/widgets/answer_box/styles/text_box.dart'; import 'package:hadi_hoda_flutter/core/widgets/answer_box/styles/text_box.dart';
import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart'; import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart';
import 'package:path_provider/path_provider.dart';
class AnswerBox extends StatelessWidget { class AnswerBox extends StatelessWidget {
const AnswerBox({super.key, required this.answer,this.selected, this.onTap, required this.index}); const AnswerBox({super.key, required this.answer,this.selected, this.onTap, required this.index});
@ -25,7 +24,7 @@ class AnswerBox extends StatelessWidget {
AnswerPictureBox( AnswerPictureBox(
selected: selected ?? false, selected: selected ?? false,
index: index, index: index,
image: '${StoragePath.applicationDir.path}/data/${answer
image: '${StoragePath.documentDir.path}/data/${answer
.imageId}${answer.imageInfo?.extension ?? '.png'}', .imageId}${answer.imageInfo?.extension ?? '.png'}',
), ),
Positioned( Positioned(

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

@ -1,11 +1,9 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
import 'package:hadi_hoda_flutter/common_ui/theme/my_theme.dart'; import 'package:hadi_hoda_flutter/common_ui/theme/my_theme.dart';
import 'package:hadi_hoda_flutter/core/utils/my_image.dart';
class AnswerPictureBox extends StatelessWidget { class AnswerPictureBox extends StatelessWidget {
const AnswerPictureBox({super.key, required this.selected, required this.image, required this.index}); const AnswerPictureBox({super.key, required this.selected, required this.image, required this.index});
@ -50,9 +48,7 @@ class AnswerPictureBox extends StatelessWidget {
), ),
child: Text( child: Text(
'$index', '$index',
style: GoogleFonts.marhey(
fontSize: 17,
fontWeight: FontWeight.w600,
style: Marhey.semiBold17.copyWith(
color: context.primaryColor, color: context.primaryColor,
), ),
), ),

8
lib/core/widgets/answer_box/styles/text_box.dart

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.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 AnswerTextBox extends StatelessWidget { class AnswerTextBox extends StatelessWidget {
const AnswerTextBox({super.key, required this.text}); const AnswerTextBox({super.key, required this.text});
@ -26,10 +26,8 @@ class AnswerTextBox extends StatelessWidget {
child: Text( child: Text(
text, text,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: GoogleFonts.marhey(
fontSize: 14,
fontWeight: FontWeight.w700,
color: Color(0XFF322386)
style: Marhey.bold14.copyWith(
color: Color(0XFF322386),
), ),
), ),
), ),

6
lib/core/widgets/showcase/question_showcase.dart

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.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/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';
@ -28,9 +28,7 @@ class QuestionShowcase extends StatelessWidget {
disableMovingAnimation: true, disableMovingAnimation: true,
textColor: context.primaryColor, textColor: context.primaryColor,
descriptionTextAlign: TextAlign.center, descriptionTextAlign: TextAlign.center,
descTextStyle: GoogleFonts.marhey(
fontSize: 12,
fontWeight: FontWeight.w700,
descTextStyle: Marhey.bold12.copyWith(
color: context.primaryColor, color: context.primaryColor,
), ),
disableScaleAnimation: true, disableScaleAnimation: true,

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

@ -1,21 +1,16 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter_archive/flutter_archive.dart'; import 'package:flutter_archive/flutter_archive.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/constants/my_constants.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/network/http_request.dart'; import 'package:hadi_hoda_flutter/core/network/http_request.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/domain/entity/level_entity.dart';
import 'package:hive/hive.dart';
import 'package:path_provider/path_provider.dart';
import 'package:hadi_hoda_flutter/core/utils/local_storage.dart';
import 'package:hadi_hoda_flutter/core/utils/storage_path.dart';
abstract class IIntroDatasource { abstract class IIntroDatasource {
Future<void> saveLevels();
Future<void> extractData();
Future<void> getFiles();
Stream<double> loadingStream(); Stream<double> loadingStream();
} }
@ -26,62 +21,49 @@ class IntroDatasourceImpl implements IIntroDatasource {
IntroDatasourceImpl(this.httpRequest); IntroDatasourceImpl(this.httpRequest);
@override @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),
);
Future<void> getFiles() async {
final String filePath = '${StoragePath.documentDir.path}/files.zip';
await levelBox.addAll(levelList);
}
} catch (e) {
throw MyException(errorMessage: '$e');
}
if (LocalStorage.readData(key: MyConstants.downloadCompleted) != 'true') {
await httpRequest.download(
urlPath: MyApi.files,
savePath: filePath,
onReceive: (count, total) {
double percent = ((count / total) * 100);
streamController.add(percent);
},
).then((value) async {
await LocalStorage.saveData(
key: MyConstants.downloadCompleted,
value: 'true',
);
});
} }
@override
Future<void> extractData() async {
try{ 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,
);
if (LocalStorage.readData(key: MyConstants.extractCompleted) != 'true') {
final File file = File(filePath);
final Directory directory = Directory('${StoragePath.documentDir.path}/files/');
await ZipFile.extractToDirectory( await ZipFile.extractToDirectory(
zipFile: file, zipFile: file,
destinationDir: dir,
destinationDir: directory,
onExtracting: (zipEntry, progress) { onExtracting: (zipEntry, progress) {
streamController.add(progress); streamController.add(progress);
return ZipFileOperation.includeItem; return ZipFileOperation.includeItem;
}, },
);
).then((value) async {
await Future.wait([
LocalStorage.saveData(
key: MyConstants.extractCompleted,
value: 'true',
),
file.delete(recursive: true),
]);
});
} else { } 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);
streamController.add(50);
await Future.delayed(Duration(milliseconds: 150)); await Future.delayed(Duration(milliseconds: 150));
streamController.add(100); streamController.add(100);
await Future.delayed(Duration(milliseconds: 150));
} }
} catch (e){ } catch (e){
throw MyException(errorMessage: '$e'); throw MyException(errorMessage: '$e');

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

@ -11,25 +11,9 @@ class IntroRepositoryImpl implements IIntroRepository {
const IntroRepositoryImpl(this.datasource); const IntroRepositoryImpl(this.datasource);
@override @override
Future<DataState<NoParams, MyException>> saveLevels() async {
Future<DataState<NoParams, MyException>> getFiles() async {
try { try {
await datasource.saveLevels();
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
Future<DataState<NoParams, MyException>> extractData() async {
try {
await datasource.extractData();
await datasource.getFiles();
return DataState.success(NoParams()); return DataState.success(NoParams());
} on MyException catch (e) { } on MyException catch (e) {
return DataState.error(e); return DataState.error(e);

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

@ -3,7 +3,6 @@ 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';
abstract class IIntroRepository { abstract class IIntroRepository {
Future<DataState<NoParams, MyException>> saveLevels();
Future<DataState<NoParams, MyException>> extractData();
Future<DataState<NoParams, MyException>> getFiles();
Stream<double> loadingStream(); Stream<double> loadingStream();
} }

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

@ -1,16 +0,0 @@
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();
}
}

6
lib/features/intro/domain/usecases/save_levels_usecase.dart → lib/features/intro/domain/usecases/get_files_usecase.dart

@ -4,13 +4,13 @@ import 'package:hadi_hoda_flutter/core/usecase/usecase.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/repository/intro_repository.dart'; import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart';
class SaveLevelsUseCase implements UseCase<NoParams, NoParams> {
class GetFilesUseCase implements UseCase<NoParams, NoParams> {
final IIntroRepository repository; final IIntroRepository repository;
const SaveLevelsUseCase(this.repository);
const GetFilesUseCase(this.repository);
@override @override
Future<DataState<NoParams, MyException>> call(NoParams params) { Future<DataState<NoParams, MyException>> call(NoParams params) {
return repository.saveLevels();
return repository.getFiles();
} }
} }

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

@ -1,30 +1,26 @@
import 'dart:async'; import 'dart:async';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hadi_hoda_flutter/core/params/no_params.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/routers/my_routes.dart';
import 'package:hadi_hoda_flutter/core/status/base_status.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/intro/domain/usecases/extract_data_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/get_files_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/loading_stream_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(
this._saveLevelsUseCase,
this._extractDataUseCase,
this._loadingStreamUseCase,
) : super(const IntroState()) {
on<SaveLevelsEvent>(_saveLevelsEvent);
on<ExtractDataEvent>(_extractDataEvent);
IntroBloc(this._getFilesUseCase, this._loadingStreamUseCase)
: super(const IntroState()) {
on<GetFilesEvent>(_getFilesEvent);
loadingStream = _loadingStreamUseCase(); loadingStream = _loadingStreamUseCase();
} }
/// ------------UseCases------------ /// ------------UseCases------------
final SaveLevelsUseCase _saveLevelsUseCase;
final ExtractDataUseCase _extractDataUseCase;
final GetFilesUseCase _getFilesUseCase;
final LoadingStreamUseCase _loadingStreamUseCase; final LoadingStreamUseCase _loadingStreamUseCase;
/// ------------Variables------------ /// ------------Variables------------
@ -35,36 +31,23 @@ class IntroBloc extends Bloc<IntroEvent, IntroState> {
/// ------------Functions------------ /// ------------Functions------------
/// ------------Api Calls------------ /// ------------Api Calls------------
FutureOr<void> _saveLevelsEvent(SaveLevelsEvent event,
Emitter<IntroState> emit) async {
await _saveLevelsUseCase(NoParams()).then(
(value) {
value.fold(
(data) async {
add(ExtractDataEvent());
},
(error) {},
);
},
);
}
FutureOr<void> _extractDataEvent(ExtractDataEvent event,
Emitter<IntroState> emit) async {
await _extractDataUseCase(NoParams()).then(
(value) {
FutureOr<void> _getFilesEvent(
GetFilesEvent event,
Emitter<IntroState> emit,
) async {
await _getFilesUseCase(NoParams()).then((value) {
value.fold( value.fold(
(data) async { (data) async {
await Future.delayed( await Future.delayed(
Duration(seconds: 1), () {
Duration(milliseconds: 300), () {
ContextProvider.context!.goNamed(Routes.homePage); ContextProvider.context!.goNamed(Routes.homePage);
}, },
); );
}, },
(error) {},
);
(error) {
emit(state.copyWith(getFilesStatus: BaseError(error.errorMessage)));
}, },
); );
});
} }
} }

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

@ -1,6 +1,4 @@
sealed class IntroEvent { sealed class IntroEvent {
const IntroEvent(); const IntroEvent();
} }
class SaveLevelsEvent extends IntroEvent {}
class ExtractDataEvent extends IntroEvent {}
class GetFilesEvent extends IntroEvent {}

8
lib/features/intro/presentation/bloc/intro_state.dart

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

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

@ -16,11 +16,10 @@ class IntroPage extends StatefulWidget {
} }
class _IntroPageState extends State<IntroPage> { class _IntroPageState extends State<IntroPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
context.read<IntroBloc>().add(SaveLevelsEvent());
context.read<IntroBloc>().add(GetFilesEvent());
} }
@override @override
@ -33,7 +32,10 @@ class _IntroPageState extends State<IntroPage> {
gradient: LinearGradient( gradient: LinearGradient(
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
colors: [Color(0XFF00154C), Color(0XFF150532)],
colors: [
Color(0XFF00154C),
Color(0XFF150532),
],
), ),
image: DecorationImage( image: DecorationImage(
image: AssetImage(MyAssets.pattern), image: AssetImage(MyAssets.pattern),
@ -47,19 +49,26 @@ class _IntroPageState extends State<IntroPage> {
), ),
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [_image(), _loading(context)],
children: [
_image(),
_loading(context),
],
), ),
), ),
); );
} }
MyImage _image() => MyImage(image: MyAssets.hadiHoda, size: 200);
MyImage _image() {
return MyImage(
image: MyAssets.hadiHoda,
size: 200,
);
}
Positioned _loading(BuildContext context) { Positioned _loading(BuildContext context) {
return Positioned( return Positioned(
bottom: MediaQuery.viewPaddingOf(context).bottom + MySpaces.s10,
bottom: MediaQuery.viewPaddingOf(context).bottom + MySpaces.s16,
child: IntroLoadingWidget( child: IntroLoadingWidget(
percent: 80,
loadingStream: context.read<IntroBloc>().loadingStream, loadingStream: context.read<IntroBloc>().loadingStream,
), ),
); );

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

@ -6,11 +6,9 @@ import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
class IntroLoadingWidget extends StatelessWidget { class IntroLoadingWidget extends StatelessWidget {
const IntroLoadingWidget({ const IntroLoadingWidget({
super.key, super.key,
this.percent,
this.loadingStream, this.loadingStream,
}); });
final double? percent;
final Stream<double>? loadingStream; final Stream<double>? loadingStream;
@override @override
@ -74,7 +72,7 @@ class IntroLoadingWidget extends StatelessWidget {
child: Center( child: Center(
child: Text( child: Text(
'${snapshot.data?.toInt() ?? 0}%', '${snapshot.data?.toInt() ?? 0}%',
style: MyTextStyle.normal17.copyWith(
style: DinoKids.regular17.copyWith(
color: Color(0XFF6E83A8), color: Color(0XFF6E83A8),
), ),
), ),

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

@ -6,7 +6,6 @@ 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/domain/entity/level_entity.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.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/bloc/level_state.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';

10
lib/features/level/presentation/ui/widgets/hint_level_widget.dart

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/utils/my_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/core/widgets/answer_box/styles/text_box.dart'; import 'package:hadi_hoda_flutter/core/widgets/answer_box/styles/text_box.dart';
@ -37,9 +37,7 @@ class HintLevelWidget extends StatelessWidget {
children: [ children: [
Text( Text(
'Step ${level.order ?? 0}', 'Step ${level.order ?? 0}',
style: GoogleFonts.marhey(
fontSize: 14,
fontWeight: FontWeight.w700,
style: Marhey.bold14.copyWith(
color: Color(0xFFD8490B), color: Color(0xFFD8490B),
), ),
), ),
@ -47,9 +45,7 @@ class HintLevelWidget extends StatelessWidget {
level.title ?? '', level.title ?? '',
maxLines: 3, maxLines: 3,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: GoogleFonts.marhey(
fontSize: 18,
fontWeight: FontWeight.w600,
style: Marhey.semiBold18.copyWith(
color: Color(0xFF322386), color: Color(0xFF322386),
height: 1, height: 1,
), ),

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

@ -42,7 +42,16 @@ 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.normal26.copyWith(color: context.primaryColor),
style: DinoKids.regular26.copyWith(
color: context.primaryColor,
shadows: [
Shadow(
color: Color(0XFF5B5B5B),
blurRadius: 2.86,
offset: Offset(0, 2),
),
],
),
), ),
if(type == LevelType.current) if(type == LevelType.current)
Positioned( Positioned(

1
lib/features/question/presentation/bloc/question_bloc.dart

@ -3,7 +3,6 @@ import 'package:bloc/bloc.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:hadi_hoda_flutter/core/params/question_params.dart'; import 'package:hadi_hoda_flutter/core/params/question_params.dart';
import 'package:hadi_hoda_flutter/core/status/base_status.dart'; import 'package:hadi_hoda_flutter/core/status/base_status.dart';
import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart';
import 'package:hadi_hoda_flutter/features/question/domain/usecases/get_level_usecase.dart'; import 'package:hadi_hoda_flutter/features/question/domain/usecases/get_level_usecase.dart';
import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_event.dart'; import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_event.dart';
import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_state.dart'; import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_state.dart';

21
lib/features/question/presentation/ui/question_page.dart

@ -1,8 +1,9 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
import 'package:hadi_hoda_flutter/common_ui/theme/my_theme.dart';
import 'package:hadi_hoda_flutter/core/utils/gap.dart'; import 'package:hadi_hoda_flutter/core/utils/gap.dart';
import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/utils/my_image.dart';
import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; import 'package:hadi_hoda_flutter/core/utils/my_localization.dart';
@ -80,10 +81,8 @@ class QuestionPage extends StatelessWidget {
BlocBuilder<QuestionBloc, QuestionState>( BlocBuilder<QuestionBloc, QuestionState>(
builder: (context, state) => Text( builder: (context, state) => Text(
state.levelEntity?.title ?? '', state.levelEntity?.title ?? '',
style: GoogleFonts.marhey(
fontSize: 14,
fontWeight: FontWeight.w700,
color: Colors.white,
style: Marhey.bold14.copyWith(
color: context.primaryColor,
), ),
), ),
), ),
@ -109,10 +108,8 @@ class QuestionPage extends StatelessWidget {
BlocBuilder<QuestionBloc, QuestionState>( BlocBuilder<QuestionBloc, QuestionState>(
builder: (context, state) => Text( builder: (context, state) => Text(
'Question ${state.currentStep} / ${state.levelEntity?.questions?.length ?? 0}', 'Question ${state.currentStep} / ${state.levelEntity?.questions?.length ?? 0}',
style: GoogleFonts.marhey(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.white.withValues(alpha: 0.5),
style: Marhey.medium12.copyWith(
color: context.primaryColor.withValues(alpha: 0.5),
shadows: [ shadows: [
Shadow( Shadow(
offset: Offset(0, 1), offset: Offset(0, 1),
@ -127,10 +124,8 @@ class QuestionPage extends StatelessWidget {
builder: (context, state) => Text( builder: (context, state) => Text(
state.levelEntity?.questions?[state.currentStep].title ?? '', state.levelEntity?.questions?[state.currentStep].title ?? '',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: GoogleFonts.marhey(
fontSize: 22,
fontWeight: FontWeight.w600,
color: Colors.white,
style: Marhey.medium12.copyWith(
color: context.primaryColor,
shadows: [ shadows: [
Shadow( Shadow(
offset: Offset(0, 1), offset: Offset(0, 1),

6
lib/features/question/presentation/ui/widgets/left_blob.dart

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/utils/my_image.dart';
class LeftBlob extends StatelessWidget { class LeftBlob extends StatelessWidget {
@ -15,9 +15,7 @@ class LeftBlob extends StatelessWidget {
Text( Text(
'Your answer\nwas not correct.', 'Your answer\nwas not correct.',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: GoogleFonts.marhey(
fontSize: 12,
fontWeight: FontWeight.w500,
style: Marhey.medium12.copyWith(
color: Color(0XFFB5AEEE), color: Color(0XFFB5AEEE),
), ),
), ),

6
lib/features/question/presentation/ui/widgets/right_blob.dart

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart';
import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart';
import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/utils/my_image.dart';
class RightBlob extends StatelessWidget { class RightBlob extends StatelessWidget {
@ -15,9 +15,7 @@ class RightBlob extends StatelessWidget {
Text( Text(
'Be more\ncareful.', 'Be more\ncareful.',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: GoogleFonts.marhey(
fontSize: 12,
fontWeight: FontWeight.w500,
style: Marhey.medium12.copyWith(
color: Color(0XFFB5AEEE), color: Color(0XFFB5AEEE),
), ),
), ),

6
lib/init_bindings.dart

@ -10,9 +10,8 @@ import 'package:hadi_hoda_flutter/features/home/domain/usecases/get_home_usecase
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/extract_data_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/get_files_usecase.dart';
import 'package:hadi_hoda_flutter/features/intro/domain/usecases/loading_stream_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/entity/level_entity.dart';
@ -47,8 +46,7 @@ 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<SaveLevelsUseCase>(() => SaveLevelsUseCase(locator()));
locator.registerLazySingleton<ExtractDataUseCase>(() => ExtractDataUseCase(locator()));
locator.registerLazySingleton<GetFilesUseCase>(() => GetFilesUseCase(locator()));
locator.registerLazySingleton<LoadingStreamUseCase>(() => LoadingStreamUseCase(locator())); locator.registerLazySingleton<LoadingStreamUseCase>(() => LoadingStreamUseCase(locator()));
/// Home Feature /// Home Feature

2
lib/main.dart

@ -14,8 +14,8 @@ Future<void> main() async {
initBindings(); initBindings();
await Future.wait([ await Future.wait([
LocalStorage.init(), LocalStorage.init(),
StoragePath.getDocumentDir(),
initDataBase(), initDataBase(),
StoragePath.getApplicationDir(),
]); ]);
AuthStorage.loadData(); AuthStorage.loadData();
runApp(const MainApp()); runApp(const MainApp());

8
pubspec.lock

@ -333,14 +333,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "16.2.4" version: "16.2.4"
google_fonts:
dependency: "direct main"
description:
name: google_fonts
sha256: "517b20870220c48752eafa0ba1a797a092fb22df0d89535fd9991e86ee2cdd9c"
url: "https://pub.dev"
source: hosted
version: "6.3.2"
graphs: graphs:
dependency: transitive dependency: transitive
description: description:

24
pubspec.yaml

@ -20,7 +20,6 @@ dependencies:
flutter_svg: ^2.2.1 flutter_svg: ^2.2.1
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
hive: ^2.2.3 hive: ^2.2.3
intl: ^0.20.2 intl: ^0.20.2
path_drawing: ^1.0.1 path_drawing: ^1.0.1
@ -41,14 +40,31 @@ flutter:
generate: true generate: true
assets: assets:
- assets/audio/
- assets/data/
- assets/fonts/ - assets/fonts/
- assets/images/ - assets/images/
- assets/json/
fonts: fonts:
- family: dinokids - family: dinokids
fonts: fonts:
- asset: assets/fonts/dinokids.ttf - asset: assets/fonts/dinokids.ttf
- family: marhey
fonts:
- asset: assets/fonts/Marhey-Light.ttf
weight: 100
- asset: assets/fonts/Marhey-Light.ttf
weight: 200
- asset: assets/fonts/Marhey-Light.ttf
weight: 300
- asset: assets/fonts/Marhey-Regular.ttf
weight: 400
- asset: assets/fonts/Marhey-Medium.ttf
weight: 500
- asset: assets/fonts/Marhey-SemiBold.ttf
weight: 600
- asset: assets/fonts/Marhey-Bold.ttf
weight: 700
- asset: assets/fonts/Marhey-Bold.ttf
weight: 800
- asset: assets/fonts/Marhey-Bold.ttf
weight: 900
Loading…
Cancel
Save