From 789f74b93d1f4b0a193ce73642421ca842a979a3 Mon Sep 17 00:00:00 2001 From: AmirrezaChegini Date: Sat, 11 Oct 2025 14:33:22 +0330 Subject: [PATCH 01/10] fix: download flow --- assets/images/loading.png | Bin 0 -> 8419 bytes lib/core/constants/my_constants.dart | 2 +- lib/core/middlewares/my_middlewares.dart | 23 +++ lib/core/routers/my_routes.dart | 12 +- .../widgets/animations/rotation_anim.dart | 50 +++++++ .../data/datasource/download_datasource.dart | 59 ++++++-- .../download_repository_impl.dart | 16 +++ .../repository/download_repository.dart | 1 + .../domain/usecases/get_images_usecase.dart | 4 +- ...ecase.dart => loading_stream_usecase.dart} | 4 +- .../presentation/bloc/download_bloc.dart | 31 +++- .../presentation/bloc/download_event.dart | 1 + .../presentation/ui/download_page.dart | 15 +- .../home/presentation/bloc/home_bloc.dart | 16 ++- .../data/datasource/intro_datasource.dart | 78 ---------- .../intro_repository_impl.dart | 34 ----- .../domain/repository/intro_repository.dart | 9 -- .../usecases/loading_stream_usecase.dart | 12 -- .../intro/presentation/bloc/intro_event.dart | 1 - .../ui/widgets/intro_loading_widget.dart | 136 ------------------ .../presentation/bloc/language_bloc.dart | 1 - lib/init_bindings.dart | 15 +- 22 files changed, 206 insertions(+), 314 deletions(-) create mode 100644 assets/images/loading.png create mode 100644 lib/core/middlewares/my_middlewares.dart create mode 100644 lib/core/widgets/animations/rotation_anim.dart rename lib/features/{intro => download}/domain/usecases/get_images_usecase.dart (78%) rename lib/features/download/domain/usecases/{loading_stream_audio_usecase.dart => loading_stream_usecase.dart} (78%) delete mode 100644 lib/features/intro/data/datasource/intro_datasource.dart delete mode 100644 lib/features/intro/data/repository_impl/intro_repository_impl.dart delete mode 100644 lib/features/intro/domain/repository/intro_repository.dart delete mode 100644 lib/features/intro/domain/usecases/loading_stream_usecase.dart delete mode 100644 lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart diff --git a/assets/images/loading.png b/assets/images/loading.png new file mode 100644 index 0000000000000000000000000000000000000000..cf919725c9f6fe30f7cfe79b94bf5f317f6f422f GIT binary patch literal 8419 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 A#{d8T literal 0 HcmV?d00001 diff --git a/lib/core/constants/my_constants.dart b/lib/core/constants/my_constants.dart index 15c0889..c157bdd 100644 --- a/lib/core/constants/my_constants.dart +++ b/lib/core/constants/my_constants.dart @@ -11,6 +11,6 @@ class MyConstants { static const String downloadedAudio = 'DOWNLOADED_AUDIO'; static const String extractedAudio = 'EXTRACTED_AUDIO'; static const String selectLanguage = 'SELECT_LANGUAGE'; - static const String firstLanguagePage = 'FIRST_LANGUAGE_PAGE'; + static const String firstDownload = 'FIRST_DOWNLOAD'; static const String currentLevel = 'CURRENT_LEVEL'; } \ No newline at end of file diff --git a/lib/core/middlewares/my_middlewares.dart b/lib/core/middlewares/my_middlewares.dart new file mode 100644 index 0000000..cad56f8 --- /dev/null +++ b/lib/core/middlewares/my_middlewares.dart @@ -0,0 +1,23 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:hadi_hoda_flutter/core/constants/my_constants.dart'; +import 'package:hadi_hoda_flutter/core/routers/my_routes.dart'; +import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; + +class MyMiddlewares { + static const MyMiddlewares _i = MyMiddlewares._internal(); + const MyMiddlewares._internal(); + factory MyMiddlewares() => _i; + + static FutureOr intro(BuildContext context, GoRouterState state) { + final String? firstDownload = LocalStorage.readData( + key: MyConstants.firstDownload); + if (firstDownload != 'true') { + return Routes.languagePage; + } else { + return null; + } + } +} diff --git a/lib/core/routers/my_routes.dart b/lib/core/routers/my_routes.dart index 0f9ef04..406fe07 100644 --- a/lib/core/routers/my_routes.dart +++ b/lib/core/routers/my_routes.dart @@ -1,7 +1,9 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:hadi_hoda_flutter/core/middlewares/my_middlewares.dart'; import 'package:hadi_hoda_flutter/core/utils/context_provider.dart'; import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_bloc.dart'; +import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_event.dart'; import 'package:hadi_hoda_flutter/features/download/presentation/ui/download_page.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'; @@ -38,8 +40,9 @@ GoRouter get appPages => GoRouter( GoRoute( name: Routes.introPage, path: Routes.introPage, + redirect: MyMiddlewares.intro, builder: (context, state) => BlocProvider( - create: (context) => IntroBloc(locator(), locator()), + create: (context) => IntroBloc(), child: const IntroPage(), ), ), @@ -47,7 +50,12 @@ GoRouter get appPages => GoRouter( name: Routes.downloadPage, path: Routes.downloadPage, builder: (context, state) => BlocProvider( - create: (context) => DownloadBloc(locator(), locator(), locator()), + create: (context) => DownloadBloc( + locator(), + locator(), + locator(), + locator(), + )..add(GetImagesEvent()), child: const DownloadPage(), ), ), diff --git a/lib/core/widgets/animations/rotation_anim.dart b/lib/core/widgets/animations/rotation_anim.dart new file mode 100644 index 0000000..ea4d9e9 --- /dev/null +++ b/lib/core/widgets/animations/rotation_anim.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; + +class RotationAnim extends StatefulWidget { + const RotationAnim({super.key, required this.child}); + + final Widget child; + + @override + State createState() => _RotationAnimState(); +} + +class _RotationAnimState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _animation; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: Duration(seconds: 20), + ); + _animation = Tween( + begin: 0, + end: 20, + ).animate(CurvedAnimation(parent: _controller, curve: Curves.linear)); + + _controller.repeat(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _controller, + child: widget.child, + builder: (context, child) => RotationTransition( + turns: _animation, + alignment: Alignment.center, + child: child, + ), + ); + } +} diff --git a/lib/features/download/data/datasource/download_datasource.dart b/lib/features/download/data/datasource/download_datasource.dart index def3308..5c3ba31 100644 --- a/lib/features/download/data/datasource/download_datasource.dart +++ b/lib/features/download/data/datasource/download_datasource.dart @@ -16,6 +16,7 @@ import 'package:hadi_hoda_flutter/features/level/domain/entity/total_data_entity import 'package:hive/hive.dart'; abstract class IDownloadDatasource { + Future getImages(); Future getAudios(); Future saveLevels(); Stream loadingStream(); @@ -27,6 +28,54 @@ class DownloadDatasourceImpl implements IDownloadDatasource { DownloadDatasourceImpl(this.httpRequest); + @override + Future getImages() async { + final String filePath = '${StoragePath.documentDir.path}/images.zip'; + + if (LocalStorage.readData(key: MyConstants.downloadedImage) != 'true') { + await httpRequest.download( + urlPath: MyApi.images, + savePath: filePath, + onReceive: (count, total) { + streamController.add(DownloadEntity( + count: count / 1, + total: total / 1, + percent: (count / total) * 100, + )); + }, + ).then((value) async { + await LocalStorage.saveData( + key: MyConstants.downloadedImage, + value: 'true', + ); + }); + } + + try{ + if (LocalStorage.readData(key: MyConstants.extractedImage) != 'true') { + final File file = File(filePath); + final Directory directory = Directory('${StoragePath.documentDir.path}/'); + await ZipFile.extractToDirectory( + zipFile: file, + destinationDir: directory, + onExtracting: (zipEntry, progress) { + return ZipFileOperation.includeItem; + }, + ).then((value) async { + await Future.wait([ + LocalStorage.saveData( + key: MyConstants.extractedImage, + value: 'true', + ), + file.delete(recursive: true), + ]); + }); + } + } catch (e){ + throw MyException(errorMessage: '$e'); + } + } + @override Future getAudios() async { final String filePath = '${StoragePath.documentDir.path}/audios.zip'; @@ -52,7 +101,7 @@ class DownloadDatasourceImpl implements IDownloadDatasource { key: MyConstants.downloadedAudio, value: 'true', ); - },); + }); } try{ @@ -76,10 +125,6 @@ class DownloadDatasourceImpl implements IDownloadDatasource { file.delete(recursive: true), ]); }); - } else { - streamController.add(DownloadEntity(percent: 50)); - await Future.delayed(Duration(milliseconds: 150)); - streamController.add(DownloadEntity(percent: 100)); } } catch (e){ throw MyException(errorMessage: '$e'); @@ -105,9 +150,7 @@ class DownloadDatasourceImpl implements IDownloadDatasource { response?['result'], (json) => LevelModel.fromJson(json), ); - await Future.wait([ - data.add(TotalDataEntity(code: selectedLanguage, levels: levels)), - ]); + await data.add(TotalDataEntity(code: selectedLanguage, levels: levels)); } } diff --git a/lib/features/download/data/repository_impl/download_repository_impl.dart b/lib/features/download/data/repository_impl/download_repository_impl.dart index 9fd8b54..13917f6 100644 --- a/lib/features/download/data/repository_impl/download_repository_impl.dart +++ b/lib/features/download/data/repository_impl/download_repository_impl.dart @@ -11,6 +11,22 @@ class DownloadRepositoryImpl implements IDownloadRepository { const DownloadRepositoryImpl(this.datasource); + @override + Future> getImages() async { + try { + await datasource.getImages(); + 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> getAudios() async { try { diff --git a/lib/features/download/domain/repository/download_repository.dart b/lib/features/download/domain/repository/download_repository.dart index b7ffbd5..ab53e49 100644 --- a/lib/features/download/domain/repository/download_repository.dart +++ b/lib/features/download/domain/repository/download_repository.dart @@ -4,6 +4,7 @@ import 'package:hadi_hoda_flutter/core/utils/data_state.dart'; import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; abstract class IDownloadRepository { + Future> getImages(); Future> getAudios(); Future> saveLevels(); Stream loadingStream(); diff --git a/lib/features/intro/domain/usecases/get_images_usecase.dart b/lib/features/download/domain/usecases/get_images_usecase.dart similarity index 78% rename from lib/features/intro/domain/usecases/get_images_usecase.dart rename to lib/features/download/domain/usecases/get_images_usecase.dart index 3774894..f91e1d6 100644 --- a/lib/features/intro/domain/usecases/get_images_usecase.dart +++ b/lib/features/download/domain/usecases/get_images_usecase.dart @@ -2,10 +2,10 @@ 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'; +import 'package:hadi_hoda_flutter/features/download/domain/repository/download_repository.dart'; class GetImagesUseCase implements UseCase { - final IIntroRepository repository; + final IDownloadRepository repository; const GetImagesUseCase(this.repository); diff --git a/lib/features/download/domain/usecases/loading_stream_audio_usecase.dart b/lib/features/download/domain/usecases/loading_stream_usecase.dart similarity index 78% rename from lib/features/download/domain/usecases/loading_stream_audio_usecase.dart rename to lib/features/download/domain/usecases/loading_stream_usecase.dart index 4af13e0..cb0fa54 100644 --- a/lib/features/download/domain/usecases/loading_stream_audio_usecase.dart +++ b/lib/features/download/domain/usecases/loading_stream_usecase.dart @@ -1,10 +1,10 @@ import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; import 'package:hadi_hoda_flutter/features/download/domain/repository/download_repository.dart'; -class LoadingStreamAudioUseCase { +class LoadingStreamUseCase { final IDownloadRepository repository; - const LoadingStreamAudioUseCase(this.repository); + const LoadingStreamUseCase(this.repository); Stream call() { return repository.loadingStream(); diff --git a/lib/features/download/presentation/bloc/download_bloc.dart b/lib/features/download/presentation/bloc/download_bloc.dart index 8672d8e..09ed742 100644 --- a/lib/features/download/presentation/bloc/download_bloc.dart +++ b/lib/features/download/presentation/bloc/download_bloc.dart @@ -2,13 +2,16 @@ import 'dart:async'; import 'package:bloc/bloc.dart'; import 'package:go_router/go_router.dart'; +import 'package:hadi_hoda_flutter/core/constants/my_constants.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/status/base_status.dart'; import 'package:hadi_hoda_flutter/core/utils/context_provider.dart'; +import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; import 'package:hadi_hoda_flutter/features/download/domain/usecases/get_audios_usecase.dart'; -import 'package:hadi_hoda_flutter/features/download/domain/usecases/loading_stream_audio_usecase.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/usecases/get_images_usecase.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/usecases/loading_stream_usecase.dart'; import 'package:hadi_hoda_flutter/features/download/domain/usecases/save_levels_usecase.dart'; import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_event.dart'; import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_state.dart'; @@ -16,20 +19,23 @@ import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_s class DownloadBloc extends Bloc { /// ------------constructor------------ DownloadBloc( + this._getImagesUseCase, this._getAudiosUseCase, this._loadingStreamUseCase, this._saveLevelsUseCase, ) : super(const DownloadState()) { + on(_getImagesEvent); on(_getAudiosEvent); on(_saveLevelsEvent); loadingStream = _loadingStreamUseCase(); } /// ------------UseCases------------ + final GetImagesUseCase _getImagesUseCase; final GetAudiosUseCase _getAudiosUseCase; final SaveLevelsUseCase _saveLevelsUseCase; - final LoadingStreamAudioUseCase _loadingStreamUseCase; + final LoadingStreamUseCase _loadingStreamUseCase; /// ------------Variables------------ Stream loadingStream = Stream.empty(); @@ -39,6 +45,22 @@ class DownloadBloc extends Bloc { /// ------------Functions------------ /// ------------Api Calls------------ + FutureOr _getImagesEvent( + GetImagesEvent event, + Emitter emit, + ) async { + await _getImagesUseCase(NoParams()).then((value) { + value.fold( + (data) { + add(GetAudiosEvent()); + }, + (error) async { + emit(state.copyWith(getFilesStatus: BaseError(error.errorMessage))); + }, + ); + }); + } + FutureOr _getAudiosEvent( GetAudiosEvent event, Emitter emit, @@ -63,7 +85,10 @@ class DownloadBloc extends Bloc { await _saveLevelsUseCase(NoParams()).then((value) => value.fold( (data) async { - ContextProvider.context.goNamed(Routes.homePage); + await LocalStorage.saveData(key: MyConstants.firstDownload, value: 'true'); + if(ContextProvider.context.mounted){ + ContextProvider.context.goNamed(Routes.homePage); + } }, (error) { emit(state.copyWith(getFilesStatus: BaseError(error.errorMessage))); diff --git a/lib/features/download/presentation/bloc/download_event.dart b/lib/features/download/presentation/bloc/download_event.dart index 9d05b54..ec62661 100644 --- a/lib/features/download/presentation/bloc/download_event.dart +++ b/lib/features/download/presentation/bloc/download_event.dart @@ -1,5 +1,6 @@ sealed class DownloadEvent { const DownloadEvent(); } +class GetImagesEvent extends DownloadEvent {} class GetAudiosEvent extends DownloadEvent {} class SaveLevelsEvent extends DownloadEvent {} diff --git a/lib/features/download/presentation/ui/download_page.dart b/lib/features/download/presentation/ui/download_page.dart index c7533b3..c289fb6 100644 --- a/lib/features/download/presentation/ui/download_page.dart +++ b/lib/features/download/presentation/ui/download_page.dart @@ -16,20 +16,9 @@ import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_e import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_state.dart'; import 'package:hadi_hoda_flutter/features/download/presentation/ui/widgets/download_loading_widget.dart'; -class DownloadPage extends StatefulWidget { +class DownloadPage extends StatelessWidget { const DownloadPage({super.key}); - @override - State createState() => _DownloadPageState(); -} - -class _DownloadPageState extends State { - @override - void initState() { - super.initState(); - context.read().add(GetAudiosEvent()); - } - @override Widget build(BuildContext context) { return Scaffold( @@ -66,7 +55,7 @@ class _DownloadPageState extends State { horizontal: 60, ), child: ErrorState( - onTap: () => context.read().add(GetAudiosEvent()), + onTap: () => context.read().add(GetImagesEvent()), ), ); } else { diff --git a/lib/features/home/presentation/bloc/home_bloc.dart b/lib/features/home/presentation/bloc/home_bloc.dart index 1ba9839..219c7ed 100644 --- a/lib/features/home/presentation/bloc/home_bloc.dart +++ b/lib/features/home/presentation/bloc/home_bloc.dart @@ -3,11 +3,15 @@ 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/constants/my_constants.dart'; import 'package:hadi_hoda_flutter/core/routers/my_routes.dart'; import 'package:hadi_hoda_flutter/core/services/audio_service.dart'; +import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; import 'package:hadi_hoda_flutter/core/widgets/about_us_dialog/about_us_dialog.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'; +import 'package:hadi_hoda_flutter/features/level/domain/entity/total_data_entity.dart'; +import 'package:hive/hive.dart'; class HomeBloc extends Bloc { /// ------------constructor------------ @@ -28,7 +32,17 @@ class HomeBloc extends Bloc { /// ------------Functions------------ void goToLevelPage(BuildContext context){ - context.pushNamed(Routes.levelPage); + final String? selectedLanguage = LocalStorage.readData(key: MyConstants.selectLanguage); + final Box dataBox = Hive.box(MyConstants.levelBox); + final TotalDataEntity findData = dataBox.values.singleWhere( + (e) => e.code == selectedLanguage, + orElse: () => TotalDataEntity(), + ); + if (findData.levels?.isNotEmpty ?? false) { + context.pushNamed(Routes.levelPage); + } else { + context.goNamed(Routes.downloadPage); + } } void goToLanguagePage(BuildContext context){ diff --git a/lib/features/intro/data/datasource/intro_datasource.dart b/lib/features/intro/data/datasource/intro_datasource.dart deleted file mode 100644 index 7a48b46..0000000 --- a/lib/features/intro/data/datasource/intro_datasource.dart +++ /dev/null @@ -1,78 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -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/error_handler/my_exception.dart'; -import 'package:hadi_hoda_flutter/core/network/http_request.dart'; -import 'package:hadi_hoda_flutter/core/utils/local_storage.dart'; -import 'package:hadi_hoda_flutter/core/utils/storage_path.dart'; -import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; - -abstract class IIntroDatasource { - Future getImages(); - Stream loadingStream(); -} - -class IntroDatasourceImpl implements IIntroDatasource { - final IHttpRequest httpRequest; - final StreamController streamController = StreamController.broadcast(); - - IntroDatasourceImpl(this.httpRequest); - - @override - Future getImages() async { - final String filePath = '${StoragePath.documentDir.path}/images.zip'; - - if (LocalStorage.readData(key: MyConstants.downloadedImage) != 'true') { - await httpRequest.download( - urlPath: MyApi.images, - savePath: filePath, - onReceive: (count, total) { - streamController.add(DownloadEntity( - count: count / 1, - total: total / 1, - percent: (count / total) * 100, - )); - }, - ).then((value) async { - await LocalStorage.saveData( - key: MyConstants.downloadedImage, - value: 'true', - ); - }); - } - - try{ - if (LocalStorage.readData(key: MyConstants.extractedImage) != 'true') { - final File file = File(filePath); - final Directory directory = Directory('${StoragePath.documentDir.path}/'); - await ZipFile.extractToDirectory( - zipFile: file, - destinationDir: directory, - onExtracting: (zipEntry, progress) { - return ZipFileOperation.includeItem; - }, - ).then((value) async { - await Future.wait([ - LocalStorage.saveData( - key: MyConstants.extractedImage, - value: 'true', - ), - file.delete(recursive: true), - ]); - }); - } else { - streamController.add(DownloadEntity(percent: 50)); - await Future.delayed(Duration(milliseconds: 150)); - streamController.add(DownloadEntity(percent: 100)); - } - } catch (e){ - throw MyException(errorMessage: '$e'); - } - } - - @override - Stream loadingStream() => streamController.stream; -} diff --git a/lib/features/intro/data/repository_impl/intro_repository_impl.dart b/lib/features/intro/data/repository_impl/intro_repository_impl.dart deleted file mode 100644 index f6e5c0d..0000000 --- a/lib/features/intro/data/repository_impl/intro_repository_impl.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/foundation.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/features/download/domain/entities/download_entity.dart'; -import 'package:hadi_hoda_flutter/features/intro/data/datasource/intro_datasource.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart'; - -class IntroRepositoryImpl implements IIntroRepository { - final IIntroDatasource datasource; - - const IntroRepositoryImpl(this.datasource); - - @override - Future> getImages() async { - try { - await datasource.getImages(); - 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 loadingStream() { - return datasource.loadingStream(); - } -} diff --git a/lib/features/intro/domain/repository/intro_repository.dart b/lib/features/intro/domain/repository/intro_repository.dart deleted file mode 100644 index 4ee9181..0000000 --- a/lib/features/intro/domain/repository/intro_repository.dart +++ /dev/null @@ -1,9 +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/utils/data_state.dart'; -import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; - -abstract class IIntroRepository { - Future> getImages(); - Stream loadingStream(); -} diff --git a/lib/features/intro/domain/usecases/loading_stream_usecase.dart b/lib/features/intro/domain/usecases/loading_stream_usecase.dart deleted file mode 100644 index 1d81d89..0000000 --- a/lib/features/intro/domain/usecases/loading_stream_usecase.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/repository/intro_repository.dart'; - -class LoadingStreamUseCase { - final IIntroRepository repository; - - const LoadingStreamUseCase(this.repository); - - Stream call() { - return repository.loadingStream(); - } -} diff --git a/lib/features/intro/presentation/bloc/intro_event.dart b/lib/features/intro/presentation/bloc/intro_event.dart index 610637a..f9f7a91 100644 --- a/lib/features/intro/presentation/bloc/intro_event.dart +++ b/lib/features/intro/presentation/bloc/intro_event.dart @@ -1,4 +1,3 @@ sealed class IntroEvent { const IntroEvent(); } -class GetImagesEvent extends IntroEvent {} diff --git a/lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart b/lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart deleted file mode 100644 index 12e4568..0000000 --- a/lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart +++ /dev/null @@ -1,136 +0,0 @@ -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'; -import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; - - -class IntroLoadingWidget extends StatelessWidget { - const IntroLoadingWidget({ - super.key, - this.loadingStream, - }); - - final Stream? 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( - initialData: DownloadEntity(), - stream: loadingStream, - builder: (context, snapshot) { - return Row( - children: [ - Expanded( - flex: 85, - child: ClipPath( - clipper: BubbleClip(), - child: AnimatedContainer( - duration: const Duration(milliseconds: 300), - padding: EdgeInsetsDirectional.only( - end: 260 - ((snapshot.data?.percent ?? 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?.percent?.toInt() ?? 0}%', - style: DinoKids.regular17.copyWith( - color: Color(0XFF6E83A8), - ), - ), - ), - ), - ], - ); - } - ), - ), - ); - } -} - -class BubbleClip extends CustomClipper { - @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 oldClipper) => false; -} diff --git a/lib/features/language/presentation/bloc/language_bloc.dart b/lib/features/language/presentation/bloc/language_bloc.dart index 364160a..f907683 100644 --- a/lib/features/language/presentation/bloc/language_bloc.dart +++ b/lib/features/language/presentation/bloc/language_bloc.dart @@ -49,7 +49,6 @@ class LanguageBloc extends Bloc { key: MyConstants.selectLanguage, value: state.selectedLang.code ?? 'fa', ), - LocalStorage.saveData(key: MyConstants.firstLanguagePage, value: 'true') ]); if (Directory( diff --git a/lib/init_bindings.dart b/lib/init_bindings.dart index 4741241..931d43b 100644 --- a/lib/init_bindings.dart +++ b/lib/init_bindings.dart @@ -9,13 +9,9 @@ import 'package:hadi_hoda_flutter/features/download/data/datasource/download_dat import 'package:hadi_hoda_flutter/features/download/data/repository_impl/download_repository_impl.dart'; import 'package:hadi_hoda_flutter/features/download/domain/repository/download_repository.dart'; import 'package:hadi_hoda_flutter/features/download/domain/usecases/get_audios_usecase.dart'; -import 'package:hadi_hoda_flutter/features/download/domain/usecases/loading_stream_audio_usecase.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/usecases/get_images_usecase.dart'; +import 'package:hadi_hoda_flutter/features/download/domain/usecases/loading_stream_usecase.dart'; import 'package:hadi_hoda_flutter/features/download/domain/usecases/save_levels_usecase.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/domain/repository/intro_repository.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/usecases/get_images_usecase.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/usecases/loading_stream_usecase.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/domain/entity/level_entity.dart'; @@ -50,17 +46,14 @@ void initBindings() { locator.registerLazySingleton(() => GetSampleUseCase(locator())); /// Intro Feature - locator.registerLazySingleton(() => IntroDatasourceImpl(locator())); - locator.registerLazySingleton(() => IntroRepositoryImpl(locator())); - locator.registerLazySingleton(() => GetImagesUseCase(locator())); - locator.registerLazySingleton(() => LoadingStreamUseCase(locator())); /// Download Feature locator.registerLazySingleton(() => DownloadDatasourceImpl(locator())); locator.registerLazySingleton(() => DownloadRepositoryImpl(locator())); + locator.registerLazySingleton(() => GetImagesUseCase(locator())); locator.registerLazySingleton(() => GetAudiosUseCase(locator())); locator.registerLazySingleton(() => SaveLevelsUseCase(locator())); - locator.registerLazySingleton(() => LoadingStreamAudioUseCase(locator())); + locator.registerLazySingleton(() => LoadingStreamUseCase(locator())); /// Language Feature From 467376083626ece905953152b26dd317eee41885 Mon Sep 17 00:00:00 2001 From: AmirrezaChegini Date: Sat, 11 Oct 2025 14:34:56 +0330 Subject: [PATCH 02/10] fix: precache images --- lib/common_ui/resources/my_assets.dart | 58 +++++++++---- .../intro/presentation/bloc/intro_bloc.dart | 65 +++++---------- .../intro/presentation/ui/intro_page.dart | 82 ++++--------------- 3 files changed, 79 insertions(+), 126 deletions(-) diff --git a/lib/common_ui/resources/my_assets.dart b/lib/common_ui/resources/my_assets.dart index 23d84bb..9b046a6 100644 --- a/lib/common_ui/resources/my_assets.dart +++ b/lib/common_ui/resources/my_assets.dart @@ -3,9 +3,28 @@ class MyAssets { const MyAssets._internal(); factory MyAssets() => _i; + /// PNG, JPG static const String backgroundHome = 'assets/images/background_intro.png'; - static const String closeBtn = 'assets/images/close_btn.svg'; static const String hadiHoda = 'assets/images/hadi_hoda.png'; + static const String khadijeLogo = 'assets/images/khadije_logo.png'; + static const String pattern = 'assets/images/pattern.png'; + static const String persons = 'assets/images/persons.png'; + static const String diamond = 'assets/images/diamond.png'; + static const String mapBackground = 'assets/images/map_background.png'; + static const String level = 'assets/images/level.png'; + static const String finishedLevel = 'assets/images/finished_level.png'; + static const String currentLevel = 'assets/images/current_level.png'; + static const String homeButton = 'assets/images/home_button.png'; + static const String error = 'assets/images/error.png'; + static const String leaf = 'assets/images/leaf.png'; + static const String happyPersons = 'assets/images/happy_persons.png'; + static const String diamondBig = 'assets/images/diamond_big.png'; + static const String ship = 'assets/images/ship.png'; + static const String shiny = 'assets/images/shiny.png'; + static const String loading = 'assets/images/loading.png'; + + /// SVG + static const String closeBtn = 'assets/images/close_btn.svg'; static const String musicOff = 'assets/images/music_off.svg'; static const String musicOn = 'assets/images/music_on.svg'; static const String button = 'assets/images/button.svg'; @@ -18,32 +37,39 @@ class MyAssets { static const String instagram = 'assets/images/instagram.svg'; static const String language = 'assets/images/language.svg'; static const String newHorizon = 'assets/images/new_horizon.svg'; - static const String khadijeLogo = 'assets/images/khadije_logo.png'; static const String home = 'assets/images/home.svg'; static const String music = 'assets/images/music.svg'; - static const String pattern = 'assets/images/pattern.png'; - static const String persons = 'assets/images/persons.png'; static const String bubbleChatLeft = 'assets/images/bubble_chat_left.svg'; static const String bubbleChatRight = 'assets/images/bubble_chat_right.svg'; - static const String diamond = 'assets/images/diamond.png'; static const String done = 'assets/images/done.svg'; static const String correct = 'assets/images/correct.svg'; static const String wrong = 'assets/images/wrong.svg'; static const String handPoint = 'assets/images/hand_point.svg'; - static const String mapBackground = 'assets/images/map_background.png'; - static const String level = 'assets/images/level.png'; - static const String finishedLevel = 'assets/images/finished_level.png'; - static const String currentLevel = 'assets/images/current_level.png'; static const String location = 'assets/images/location.svg'; static const String play = 'assets/images/play.svg'; - static const String homeButton = 'assets/images/home_button.png'; static const String doneRounded = 'assets/images/done_rounded.svg'; static const String lang = 'assets/images/lang.svg'; - static const String error = 'assets/images/error.png'; - static const String leaf = 'assets/images/leaf.png'; - static const String happyPersons = 'assets/images/happy_persons.png'; - static const String diamondBig = 'assets/images/diamond_big.png'; - static const String ship = 'assets/images/ship.png'; - static const String shiny = 'assets/images/shiny.png'; static const String unMusic = 'assets/images/unmusic.svg'; + + + static final List images = [ + backgroundHome, + hadiHoda, + khadijeLogo, + pattern, + persons, + diamond, + mapBackground, + level, + finishedLevel, + currentLevel, + homeButton, + error, + leaf, + happyPersons, + diamondBig, + ship, + shiny, + loading, + ]; } \ No newline at end of file diff --git a/lib/features/intro/presentation/bloc/intro_bloc.dart b/lib/features/intro/presentation/bloc/intro_bloc.dart index 0bc25e2..2de2b84 100644 --- a/lib/features/intro/presentation/bloc/intro_bloc.dart +++ b/lib/features/intro/presentation/bloc/intro_bloc.dart @@ -1,64 +1,39 @@ -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/constants/my_constants.dart'; -import 'package:hadi_hoda_flutter/core/params/no_params.dart'; +import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.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/local_storage.dart'; -import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/usecases/get_images_usecase.dart'; -import 'package:hadi_hoda_flutter/features/intro/domain/usecases/loading_stream_usecase.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'; class IntroBloc extends Bloc { /// ------------constructor------------ - IntroBloc( - this._getImagesUseCase, - this._loadingStreamUseCase, - ) - : super(const IntroState()) { - on(_getImagesEvent); - loadingStream = _loadingStreamUseCase(); - } + IntroBloc() : super(const IntroState()); /// ------------UseCases------------ - final GetImagesUseCase _getImagesUseCase; - final LoadingStreamUseCase _loadingStreamUseCase; /// ------------Variables------------ - Stream loadingStream = Stream.empty(); /// ------------Controllers------------ /// ------------Functions------------ + Future _precacheAllImages(BuildContext context) async { + await Future.wait( + MyAssets.images.map( + (assetPath) => precacheImage(AssetImage(assetPath), context), + ), + ); + } - /// ------------Api Calls------------ - FutureOr _getImagesEvent( - GetImagesEvent event, - Emitter emit, - ) async { - emit(state.copyWith(getFilesStatus: BaseInit())); - await _getImagesUseCase(NoParams()).then((value) { - value.fold( - (data) async { - if(LocalStorage.readData(key: MyConstants.firstLanguagePage) != 'true'){ - ContextProvider.context.goNamed(Routes.languagePage); - } else { - await Future.delayed(Duration(milliseconds: 500), () { - if (ContextProvider.context.mounted) { - ContextProvider.context.goNamed(Routes.homePage); - } - }); - } - }, - (error) async { - emit(state.copyWith(getFilesStatus: BaseError(error.errorMessage))); - }, - ); - }); + Future goToHomePage(BuildContext context) async { + if (context.mounted) { + await _precacheAllImages(context); + } + + if (context.mounted) { + context.goNamed(Routes.homePage); + } } + + /// ------------Api Calls------------ } diff --git a/lib/features/intro/presentation/ui/intro_page.dart b/lib/features/intro/presentation/ui/intro_page.dart index 8d0d38b..3bed0a4 100644 --- a/lib/features/intro/presentation/ui/intro_page.dart +++ b/lib/features/intro/presentation/ui/intro_page.dart @@ -1,20 +1,11 @@ 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/resources/my_colors.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; -import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; -import 'package:hadi_hoda_flutter/core/status/base_status.dart'; -import 'package:hadi_hoda_flutter/core/utils/convert_size.dart'; import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; -import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; -import 'package:hadi_hoda_flutter/core/widgets/error/error_state.dart'; -import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; +import 'package:hadi_hoda_flutter/core/widgets/animations/rotation_anim.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/bloc/intro_state.dart'; -import 'package:hadi_hoda_flutter/features/intro/presentation/ui/widgets/intro_loading_widget.dart'; class IntroPage extends StatefulWidget { const IntroPage({super.key}); @@ -27,9 +18,12 @@ class _IntroPageState extends State { @override void initState() { super.initState(); - context.read().add(GetImagesEvent()); + WidgetsBinding.instance.addPostFrameCallback((_) { + context.read().goToHomePage(context); + }); } + @override Widget build(BuildContext context) { return Scaffold( @@ -55,31 +49,12 @@ class _IntroPageState extends State { ), ), ), - child: BlocBuilder( - buildWhen: (previous, current) => - previous.getFilesStatus != current.getFilesStatus, - builder: (context, state) { - if (state.getFilesStatus is BaseError) { - return Padding( - padding: EdgeInsets.symmetric( - vertical: MediaQuery.viewPaddingOf(context).bottom + MySpaces.s16, - horizontal: 60, - ), - child: ErrorState( - onTap: () => context.read().add(GetImagesEvent()), - ), - ); - } else { - return Stack( - alignment: Alignment.center, - children: [ - _image(), - _text(context), - _loading(context), - ], - ); - } - } + child: Stack( + alignment: Alignment.center, + children: [ + _image(), + _loading(context), + ], ), ), ); @@ -92,39 +67,16 @@ class _IntroPageState extends State { ); } - Widget _text(BuildContext context) { - return PositionedDirectional( - bottom: 130, - child: Column( - spacing: MySpaces.s6, - children: [ - Text( - context.translate.please_wait, - style: Marhey.medium22.copyWith( - color: MyColors.white, - ), - ), - StreamBuilder( - initialData: DownloadEntity(), - stream: context.read().loadingStream, - builder: (context, snapshot) => Text( - '${context.translate.downloading_data} (${snapshot.data?.count.toMB ?? 0.0}mb / ${snapshot.data?.total.toMB ?? 0.0}mb)', - style: Marhey.medium12.copyWith( - color: MyColors.white, - ), - ), - ), - ], - ), - ); - } Positioned _loading(BuildContext context) { return Positioned( bottom: MediaQuery.viewPaddingOf(context).bottom + MySpaces.s16, - child: IntroLoadingWidget( - loadingStream: context.read().loadingStream, - ), + child: RotationAnim( + child: MyImage( + image: MyAssets.loading, + size: 70, + ), + ) ); } } From 50bbd9a772942dc8320b2fee59f23e1d48b4f19c Mon Sep 17 00:00:00 2001 From: AmirrezaChegini Date: Sat, 11 Oct 2025 14:58:31 +0330 Subject: [PATCH 03/10] fix: precache svg and remove svg picture --- assets/{images => svg}/bubble_chat_left.svg | 0 assets/{images => svg}/bubble_chat_right.svg | 0 assets/{images => svg}/button.svg | 0 assets/{images => svg}/button_2.svg | 0 assets/{images => svg}/button_3.svg | 0 assets/{images => svg}/close_btn.svg | 0 assets/{images => svg}/correct.svg | 0 assets/{images => svg}/done.svg | 0 assets/{images => svg}/done_rounded.svg | 0 assets/{images => svg}/facebook.svg | 0 assets/{images => svg}/hand_point.svg | 0 assets/{images => svg}/home.svg | 0 assets/{images => svg}/instagram.svg | 0 assets/{images => svg}/lang.svg | 0 assets/{images => svg}/language.svg | 0 assets/{images => svg}/location.svg | 0 assets/{images => svg}/music.svg | 0 assets/{images => svg}/music_off.svg | 0 assets/{images => svg}/music_on.svg | 0 assets/{images => svg}/new_horizon.svg | 0 assets/{images => svg}/play.svg | 0 assets/{images => svg}/theme.svg | 0 assets/{images => svg}/unmusic.svg | 0 assets/{images => svg}/whatsapp.svg | 0 assets/{images => svg}/wrong.svg | 0 assets/{images => svg}/youtube.svg | 0 lib/common_ui/resources/my_assets.dart | 52 +++++++++---------- .../about_us_dialog/about_us_dialog.dart | 2 +- .../answer_box/styles/picture_box.dart | 2 +- lib/core/widgets/button/my_button.dart | 2 +- lib/core/widgets/error/error_state.dart | 2 +- .../widgets/hadith_dialog/hadith_dialog.dart | 2 +- .../{utils => widgets/images}/my_image.dart | 6 +-- .../widgets/showcase/question_showcase.dart | 2 +- .../presentation/ui/download_page.dart | 2 +- .../home/presentation/ui/home_page.dart | 2 +- .../intro/presentation/bloc/intro_bloc.dart | 11 ++-- .../intro/presentation/ui/intro_page.dart | 2 +- .../presentation/ui/language_page.dart | 2 +- .../level/presentation/ui/level_page.dart | 2 +- .../ui/widgets/hint_level_widget.dart | 2 +- .../presentation/ui/widgets/level_widget.dart | 2 +- .../ui/screens/diamond_screen.dart | 2 +- .../ui/screens/question_screen.dart | 2 +- .../ui/widgets/glassy_button.dart | 2 +- .../presentation/ui/widgets/left_blob.dart | 2 +- .../ui/widgets/question_stepper.dart | 2 +- .../presentation/ui/widgets/right_blob.dart | 2 +- pubspec.lock | 12 +---- pubspec.yaml | 6 ++- 50 files changed, 63 insertions(+), 62 deletions(-) rename assets/{images => svg}/bubble_chat_left.svg (100%) rename assets/{images => svg}/bubble_chat_right.svg (100%) rename assets/{images => svg}/button.svg (100%) rename assets/{images => svg}/button_2.svg (100%) rename assets/{images => svg}/button_3.svg (100%) rename assets/{images => svg}/close_btn.svg (100%) rename assets/{images => svg}/correct.svg (100%) rename assets/{images => svg}/done.svg (100%) rename assets/{images => svg}/done_rounded.svg (100%) rename assets/{images => svg}/facebook.svg (100%) rename assets/{images => svg}/hand_point.svg (100%) rename assets/{images => svg}/home.svg (100%) rename assets/{images => svg}/instagram.svg (100%) rename assets/{images => svg}/lang.svg (100%) rename assets/{images => svg}/language.svg (100%) rename assets/{images => svg}/location.svg (100%) rename assets/{images => svg}/music.svg (100%) rename assets/{images => svg}/music_off.svg (100%) rename assets/{images => svg}/music_on.svg (100%) rename assets/{images => svg}/new_horizon.svg (100%) rename assets/{images => svg}/play.svg (100%) rename assets/{images => svg}/theme.svg (100%) rename assets/{images => svg}/unmusic.svg (100%) rename assets/{images => svg}/whatsapp.svg (100%) rename assets/{images => svg}/wrong.svg (100%) rename assets/{images => svg}/youtube.svg (100%) rename lib/core/{utils => widgets/images}/my_image.dart (85%) diff --git a/assets/images/bubble_chat_left.svg b/assets/svg/bubble_chat_left.svg similarity index 100% rename from assets/images/bubble_chat_left.svg rename to assets/svg/bubble_chat_left.svg diff --git a/assets/images/bubble_chat_right.svg b/assets/svg/bubble_chat_right.svg similarity index 100% rename from assets/images/bubble_chat_right.svg rename to assets/svg/bubble_chat_right.svg diff --git a/assets/images/button.svg b/assets/svg/button.svg similarity index 100% rename from assets/images/button.svg rename to assets/svg/button.svg diff --git a/assets/images/button_2.svg b/assets/svg/button_2.svg similarity index 100% rename from assets/images/button_2.svg rename to assets/svg/button_2.svg diff --git a/assets/images/button_3.svg b/assets/svg/button_3.svg similarity index 100% rename from assets/images/button_3.svg rename to assets/svg/button_3.svg diff --git a/assets/images/close_btn.svg b/assets/svg/close_btn.svg similarity index 100% rename from assets/images/close_btn.svg rename to assets/svg/close_btn.svg diff --git a/assets/images/correct.svg b/assets/svg/correct.svg similarity index 100% rename from assets/images/correct.svg rename to assets/svg/correct.svg diff --git a/assets/images/done.svg b/assets/svg/done.svg similarity index 100% rename from assets/images/done.svg rename to assets/svg/done.svg diff --git a/assets/images/done_rounded.svg b/assets/svg/done_rounded.svg similarity index 100% rename from assets/images/done_rounded.svg rename to assets/svg/done_rounded.svg diff --git a/assets/images/facebook.svg b/assets/svg/facebook.svg similarity index 100% rename from assets/images/facebook.svg rename to assets/svg/facebook.svg diff --git a/assets/images/hand_point.svg b/assets/svg/hand_point.svg similarity index 100% rename from assets/images/hand_point.svg rename to assets/svg/hand_point.svg diff --git a/assets/images/home.svg b/assets/svg/home.svg similarity index 100% rename from assets/images/home.svg rename to assets/svg/home.svg diff --git a/assets/images/instagram.svg b/assets/svg/instagram.svg similarity index 100% rename from assets/images/instagram.svg rename to assets/svg/instagram.svg diff --git a/assets/images/lang.svg b/assets/svg/lang.svg similarity index 100% rename from assets/images/lang.svg rename to assets/svg/lang.svg diff --git a/assets/images/language.svg b/assets/svg/language.svg similarity index 100% rename from assets/images/language.svg rename to assets/svg/language.svg diff --git a/assets/images/location.svg b/assets/svg/location.svg similarity index 100% rename from assets/images/location.svg rename to assets/svg/location.svg diff --git a/assets/images/music.svg b/assets/svg/music.svg similarity index 100% rename from assets/images/music.svg rename to assets/svg/music.svg diff --git a/assets/images/music_off.svg b/assets/svg/music_off.svg similarity index 100% rename from assets/images/music_off.svg rename to assets/svg/music_off.svg diff --git a/assets/images/music_on.svg b/assets/svg/music_on.svg similarity index 100% rename from assets/images/music_on.svg rename to assets/svg/music_on.svg diff --git a/assets/images/new_horizon.svg b/assets/svg/new_horizon.svg similarity index 100% rename from assets/images/new_horizon.svg rename to assets/svg/new_horizon.svg diff --git a/assets/images/play.svg b/assets/svg/play.svg similarity index 100% rename from assets/images/play.svg rename to assets/svg/play.svg diff --git a/assets/images/theme.svg b/assets/svg/theme.svg similarity index 100% rename from assets/images/theme.svg rename to assets/svg/theme.svg diff --git a/assets/images/unmusic.svg b/assets/svg/unmusic.svg similarity index 100% rename from assets/images/unmusic.svg rename to assets/svg/unmusic.svg diff --git a/assets/images/whatsapp.svg b/assets/svg/whatsapp.svg similarity index 100% rename from assets/images/whatsapp.svg rename to assets/svg/whatsapp.svg diff --git a/assets/images/wrong.svg b/assets/svg/wrong.svg similarity index 100% rename from assets/images/wrong.svg rename to assets/svg/wrong.svg diff --git a/assets/images/youtube.svg b/assets/svg/youtube.svg similarity index 100% rename from assets/images/youtube.svg rename to assets/svg/youtube.svg diff --git a/lib/common_ui/resources/my_assets.dart b/lib/common_ui/resources/my_assets.dart index 9b046a6..7fbb1e5 100644 --- a/lib/common_ui/resources/my_assets.dart +++ b/lib/common_ui/resources/my_assets.dart @@ -24,32 +24,32 @@ class MyAssets { static const String loading = 'assets/images/loading.png'; /// SVG - static const String closeBtn = 'assets/images/close_btn.svg'; - static const String musicOff = 'assets/images/music_off.svg'; - static const String musicOn = 'assets/images/music_on.svg'; - static const String button = 'assets/images/button.svg'; - static const String button2 = 'assets/images/button_2.svg'; - static const String button3 = 'assets/images/button_3.svg'; - static const String theme = 'assets/images/theme.svg'; - static const String facebook = 'assets/images/facebook.svg'; - static const String whatsapp = 'assets/images/whatsapp.svg'; - static const String youtube = 'assets/images/youtube.svg'; - static const String instagram = 'assets/images/instagram.svg'; - static const String language = 'assets/images/language.svg'; - static const String newHorizon = 'assets/images/new_horizon.svg'; - static const String home = 'assets/images/home.svg'; - static const String music = 'assets/images/music.svg'; - static const String bubbleChatLeft = 'assets/images/bubble_chat_left.svg'; - static const String bubbleChatRight = 'assets/images/bubble_chat_right.svg'; - static const String done = 'assets/images/done.svg'; - static const String correct = 'assets/images/correct.svg'; - static const String wrong = 'assets/images/wrong.svg'; - static const String handPoint = 'assets/images/hand_point.svg'; - static const String location = 'assets/images/location.svg'; - static const String play = 'assets/images/play.svg'; - static const String doneRounded = 'assets/images/done_rounded.svg'; - static const String lang = 'assets/images/lang.svg'; - static const String unMusic = 'assets/images/unmusic.svg'; + static const String closeBtn = 'assets/svg/close_btn.svg'; + static const String musicOff = 'assets/svg/music_off.svg'; + static const String musicOn = 'assets/svg/music_on.svg'; + static const String button = 'assets/svg/button.svg'; + static const String button2 = 'assets/svg/button_2.svg'; + static const String button3 = 'assets/svg/button_3.svg'; + static const String theme = 'assets/svg/theme.svg'; + static const String facebook = 'assets/svg/facebook.svg'; + static const String whatsapp = 'assets/svg/whatsapp.svg'; + static const String youtube = 'assets/svg/youtube.svg'; + static const String instagram = 'assets/svg/instagram.svg'; + static const String language = 'assets/svg/language.svg'; + static const String newHorizon = 'assets/svg/new_horizon.svg'; + static const String home = 'assets/svg/home.svg'; + static const String music = 'assets/svg/music.svg'; + static const String bubbleChatLeft = 'assets/svg/bubble_chat_left.svg'; + static const String bubbleChatRight = 'assets/svg/bubble_chat_right.svg'; + static const String done = 'assets/svg/done.svg'; + static const String correct = 'assets/svg/correct.svg'; + static const String wrong = 'assets/svg/wrong.svg'; + static const String handPoint = 'assets/svg/hand_point.svg'; + static const String location = 'assets/svg/location.svg'; + static const String play = 'assets/svg/play.svg'; + static const String doneRounded = 'assets/svg/done_rounded.svg'; + static const String lang = 'assets/svg/lang.svg'; + static const String unMusic = 'assets/svg/unmusic.svg'; static final List images = [ diff --git a/lib/core/widgets/about_us_dialog/about_us_dialog.dart b/lib/core/widgets/about_us_dialog/about_us_dialog.dart index ec274f3..5b44a9b 100644 --- a/lib/core/widgets/about_us_dialog/about_us_dialog.dart +++ b/lib/core/widgets/about_us_dialog/about_us_dialog.dart @@ -6,9 +6,9 @@ import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; import 'package:hadi_hoda_flutter/core/utils/check_platform.dart'; -import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; import 'package:hadi_hoda_flutter/core/widgets/about_us_dialog/styles/background.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; Future showAboutUsDialog({required BuildContext context}) async { await showDialog( diff --git a/lib/core/widgets/answer_box/styles/picture_box.dart b/lib/core/widgets/answer_box/styles/picture_box.dart index 64550c0..b1b1976 100644 --- a/lib/core/widgets/answer_box/styles/picture_box.dart +++ b/lib/core/widgets/answer_box/styles/picture_box.dart @@ -7,7 +7,7 @@ import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; -import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; import 'package:hadi_hoda_flutter/features/question/presentation/ui/widgets/black_white_effect.dart'; class AnswerPictureBox extends StatefulWidget { diff --git a/lib/core/widgets/button/my_button.dart b/lib/core/widgets/button/my_button.dart index a0b48fd..a399435 100644 --- a/lib/core/widgets/button/my_button.dart +++ b/lib/core/widgets/button/my_button.dart @@ -3,8 +3,8 @@ import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; -import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/widgets/button/enum/button_type.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; class MyButton extends StatelessWidget { const MyButton({ diff --git a/lib/core/widgets/error/error_state.dart b/lib/core/widgets/error/error_state.dart index d39e965..e23542d 100644 --- a/lib/core/widgets/error/error_state.dart +++ b/lib/core/widgets/error/error_state.dart @@ -4,8 +4,8 @@ import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; import 'package:hadi_hoda_flutter/core/utils/gap.dart'; -import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; class ErrorState extends StatelessWidget { const ErrorState({super.key, this.onTap}); diff --git a/lib/core/widgets/hadith_dialog/hadith_dialog.dart b/lib/core/widgets/hadith_dialog/hadith_dialog.dart index ff5d098..eaa2227 100644 --- a/lib/core/widgets/hadith_dialog/hadith_dialog.dart +++ b/lib/core/widgets/hadith_dialog/hadith_dialog.dart @@ -7,8 +7,8 @@ import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; import 'package:hadi_hoda_flutter/core/utils/check_platform.dart'; -import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/widgets/about_us_dialog/styles/background.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; Future showHadithDialog({required BuildContext context}) async { await showDialog( diff --git a/lib/core/utils/my_image.dart b/lib/core/widgets/images/my_image.dart similarity index 85% rename from lib/core/utils/my_image.dart rename to lib/core/widgets/images/my_image.dart index bf977e3..b3b8cd8 100644 --- a/lib/core/utils/my_image.dart +++ b/lib/core/widgets/images/my_image.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; +import 'package:vector_graphics/vector_graphics.dart'; class MyImage extends StatelessWidget { const MyImage({ @@ -25,8 +25,8 @@ class MyImage extends StatelessWidget { height: size, ); } else { - return SvgPicture.asset( - image, + return VectorGraphic( + loader: AssetBytesLoader(image), fit: fit ?? BoxFit.contain, width: size, height: size, diff --git a/lib/core/widgets/showcase/question_showcase.dart b/lib/core/widgets/showcase/question_showcase.dart index 79c4f5b..03be7c0 100644 --- a/lib/core/widgets/showcase/question_showcase.dart +++ b/lib/core/widgets/showcase/question_showcase.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.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/widgets/images/my_image.dart'; import 'package:showcaseview/showcaseview.dart'; class QuestionShowcase extends StatelessWidget { diff --git a/lib/features/download/presentation/ui/download_page.dart b/lib/features/download/presentation/ui/download_page.dart index c289fb6..4d6d1ba 100644 --- a/lib/features/download/presentation/ui/download_page.dart +++ b/lib/features/download/presentation/ui/download_page.dart @@ -6,10 +6,10 @@ 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/status/base_status.dart'; import 'package:hadi_hoda_flutter/core/utils/convert_size.dart'; -import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; import 'package:hadi_hoda_flutter/core/widgets/error/error_state.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; import 'package:hadi_hoda_flutter/features/download/domain/entities/download_entity.dart'; import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_bloc.dart'; import 'package:hadi_hoda_flutter/features/download/presentation/bloc/download_event.dart'; diff --git a/lib/features/home/presentation/ui/home_page.dart b/lib/features/home/presentation/ui/home_page.dart index 2905c4f..9b6490d 100644 --- a/lib/features/home/presentation/ui/home_page.dart +++ b/lib/features/home/presentation/ui/home_page.dart @@ -2,11 +2,11 @@ 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/resources/my_spaces.dart'; -import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; import 'package:hadi_hoda_flutter/core/widgets/button/enum/button_type.dart'; import 'package:hadi_hoda_flutter/core/widgets/button/my_button.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; import 'package:hadi_hoda_flutter/features/home/presentation/bloc/home_bloc.dart'; class HomePage extends StatelessWidget { diff --git a/lib/features/intro/presentation/bloc/intro_bloc.dart b/lib/features/intro/presentation/bloc/intro_bloc.dart index 2de2b84..8ec0092 100644 --- a/lib/features/intro/presentation/bloc/intro_bloc.dart +++ b/lib/features/intro/presentation/bloc/intro_bloc.dart @@ -30,9 +30,14 @@ class IntroBloc extends Bloc { await _precacheAllImages(context); } - if (context.mounted) { - context.goNamed(Routes.homePage); - } + await Future.delayed( + Duration(seconds: 2), + () { + if (context.mounted) { + context.goNamed(Routes.homePage); + } + }, + ); } /// ------------Api Calls------------ diff --git a/lib/features/intro/presentation/ui/intro_page.dart b/lib/features/intro/presentation/ui/intro_page.dart index 3bed0a4..fd3cdcf 100644 --- a/lib/features/intro/presentation/ui/intro_page.dart +++ b/lib/features/intro/presentation/ui/intro_page.dart @@ -2,9 +2,9 @@ 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/resources/my_spaces.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/widgets/animations/rotation_anim.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; import 'package:hadi_hoda_flutter/features/intro/presentation/bloc/intro_bloc.dart'; class IntroPage extends StatefulWidget { diff --git a/lib/features/language/presentation/ui/language_page.dart b/lib/features/language/presentation/ui/language_page.dart index cd5af65..957f1bc 100644 --- a/lib/features/language/presentation/ui/language_page.dart +++ b/lib/features/language/presentation/ui/language_page.dart @@ -4,10 +4,10 @@ import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; -import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; import 'package:hadi_hoda_flutter/core/widgets/button/my_button.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_bloc.dart'; import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_event.dart'; import 'package:hadi_hoda_flutter/features/language/presentation/bloc/language_state.dart'; diff --git a/lib/features/level/presentation/ui/level_page.dart b/lib/features/level/presentation/ui/level_page.dart index c9b774e..92f7c14 100644 --- a/lib/features/level/presentation/ui/level_page.dart +++ b/lib/features/level/presentation/ui/level_page.dart @@ -2,8 +2,8 @@ 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/resources/my_spaces.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/widgets/images/my_image.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_state.dart'; diff --git a/lib/features/level/presentation/ui/widgets/hint_level_widget.dart b/lib/features/level/presentation/ui/widgets/hint_level_widget.dart index c5a95bd..4c0c474 100644 --- a/lib/features/level/presentation/ui/widgets/hint_level_widget.dart +++ b/lib/features/level/presentation/ui/widgets/hint_level_widget.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.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_text_style.dart'; -import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; import 'package:hadi_hoda_flutter/core/widgets/answer_box/styles/text_box.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; class HintLevelWidget extends StatelessWidget { diff --git a/lib/features/level/presentation/ui/widgets/level_widget.dart b/lib/features/level/presentation/ui/widgets/level_widget.dart index fea8e5a..2850a82 100644 --- a/lib/features/level/presentation/ui/widgets/level_widget.dart +++ b/lib/features/level/presentation/ui/widgets/level_widget.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; -import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; enum LevelType { diff --git a/lib/features/question/presentation/ui/screens/diamond_screen.dart b/lib/features/question/presentation/ui/screens/diamond_screen.dart index 4cd796c..dc66891 100644 --- a/lib/features/question/presentation/ui/screens/diamond_screen.dart +++ b/lib/features/question/presentation/ui/screens/diamond_screen.dart @@ -6,11 +6,11 @@ import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; import 'package:hadi_hoda_flutter/core/utils/gap.dart'; -import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; import 'package:hadi_hoda_flutter/core/widgets/button/enum/button_type.dart'; import 'package:hadi_hoda_flutter/core/widgets/button/my_button.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_bloc.dart'; import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_event.dart'; diff --git a/lib/features/question/presentation/ui/screens/question_screen.dart b/lib/features/question/presentation/ui/screens/question_screen.dart index c4ec545..6d92073 100644 --- a/lib/features/question/presentation/ui/screens/question_screen.dart +++ b/lib/features/question/presentation/ui/screens/question_screen.dart @@ -5,9 +5,9 @@ import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; import 'package:hadi_hoda_flutter/core/utils/gap.dart'; -import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; import 'package:hadi_hoda_flutter/core/widgets/answer_box/answer_box.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; import 'package:hadi_hoda_flutter/core/widgets/showcase/question_showcase.dart'; import 'package:hadi_hoda_flutter/features/question/domain/entity/answer_entity.dart'; import 'package:hadi_hoda_flutter/features/question/presentation/bloc/question_bloc.dart'; diff --git a/lib/features/question/presentation/ui/widgets/glassy_button.dart b/lib/features/question/presentation/ui/widgets/glassy_button.dart index b3beb7b..211b00b 100644 --- a/lib/features/question/presentation/ui/widgets/glassy_button.dart +++ b/lib/features/question/presentation/ui/widgets/glassy_button.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; -import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; class GlassyButton extends StatelessWidget { const GlassyButton({super.key, required this.image, this.onTap}); diff --git a/lib/features/question/presentation/ui/widgets/left_blob.dart b/lib/features/question/presentation/ui/widgets/left_blob.dart index f09256b..3f53a61 100644 --- a/lib/features/question/presentation/ui/widgets/left_blob.dart +++ b/lib/features/question/presentation/ui/widgets/left_blob.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.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_localization.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; class LeftBlob extends StatelessWidget { const LeftBlob({super.key}); diff --git a/lib/features/question/presentation/ui/widgets/question_stepper.dart b/lib/features/question/presentation/ui/widgets/question_stepper.dart index bcb4a39..d7408b4 100644 --- a/lib/features/question/presentation/ui/widgets/question_stepper.dart +++ b/lib/features/question/presentation/ui/widgets/question_stepper.dart @@ -1,7 +1,7 @@ import 'package:easy_stepper/easy_stepper.dart'; import 'package:flutter/material.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_assets.dart'; -import 'package:hadi_hoda_flutter/core/utils/my_image.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; class QuestionStepper extends StatelessWidget { const QuestionStepper({super.key, required this.length ,required this.currentStep}); diff --git a/lib/features/question/presentation/ui/widgets/right_blob.dart b/lib/features/question/presentation/ui/widgets/right_blob.dart index 03f4268..168395b 100644 --- a/lib/features/question/presentation/ui/widgets/right_blob.dart +++ b/lib/features/question/presentation/ui/widgets/right_blob.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.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_localization.dart'; +import 'package:hadi_hoda_flutter/core/widgets/images/my_image.dart'; class RightBlob extends StatelessWidget { const RightBlob({super.key}); diff --git a/pubspec.lock b/pubspec.lock index 1b61d45..f8bbe92 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -299,14 +299,6 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_svg: - dependency: "direct main" - description: - name: flutter_svg - sha256: b9c2ad5872518a27507ab432d1fb97e8813b05f0fc693f9d40fad06d073e0678 - url: "https://pub.dev" - source: hosted - version: "2.2.1" flutter_test: dependency: "direct dev" description: flutter @@ -899,7 +891,7 @@ packages: source: hosted version: "4.5.1" vector_graphics: - dependency: transitive + dependency: "direct main" description: name: vector_graphics sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 @@ -915,7 +907,7 @@ packages: source: hosted version: "1.1.13" vector_graphics_compiler: - dependency: transitive + dependency: "direct dev" description: name: vector_graphics_compiler sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc diff --git a/pubspec.yaml b/pubspec.yaml index 84f89c0..52ef959 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -18,7 +18,6 @@ dependencies: flutter_bloc: ^9.1.1 flutter_localizations: sdk: flutter - flutter_svg: ^2.2.1 get_it: ^8.2.0 go_router: ^16.1.0 hive: ^2.2.3 @@ -29,6 +28,7 @@ dependencies: pretty_dio_logger: ^1.4.0 shared_preferences: ^2.5.3 showcaseview: ^4.0.1 + vector_graphics: ^1.1.19 dev_dependencies: flutter_test: @@ -36,6 +36,7 @@ dev_dependencies: flutter_lints: ^5.0.0 hive_generator: ^2.0.1 build_runner: ^2.4.13 + vector_graphics_compiler: ^1.1.19 flutter: uses-material-design: true @@ -44,6 +45,9 @@ flutter: assets: - assets/fonts/ - assets/images/ + - path: assets/svg/ + transformers: + - package: vector_graphics_compiler fonts: From c707a424f78ac5f3e66b59d22a1c23e1bec1ffac Mon Sep 17 00:00:00 2001 From: AmirrezaChegini Date: Sat, 11 Oct 2025 15:31:35 +0330 Subject: [PATCH 04/10] fix: bottom path responsive --- .../level/presentation/bloc/level_bloc.dart | 2 +- .../level/presentation/ui/level_page.dart | 5 +---- .../presentation/ui/widgets/bottom_path.dart | 22 +++++++------------ 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/lib/features/level/presentation/bloc/level_bloc.dart b/lib/features/level/presentation/bloc/level_bloc.dart index ef5cde4..1fcf20e 100644 --- a/lib/features/level/presentation/bloc/level_bloc.dart +++ b/lib/features/level/presentation/bloc/level_bloc.dart @@ -43,7 +43,7 @@ class LevelBloc extends Bloc { LevelLocation(bottom: 50, left: 100, index: 2), LevelLocation(bottom: 150, left: 60, index: 3), LevelLocation(bottom: 210, left: 110, index: 4), - LevelLocation(bottom: 250, right: 60, index: 5), + LevelLocation(bottom: 260, right: 70, index: 5), LevelLocation(top: 170, right: 40, index: 6), LevelLocation(top: 70, right: 70, index: 7), LevelLocation(top: -20, right: 70, index: 8), diff --git a/lib/features/level/presentation/ui/level_page.dart b/lib/features/level/presentation/ui/level_page.dart index 92f7c14..edf3b25 100644 --- a/lib/features/level/presentation/ui/level_page.dart +++ b/lib/features/level/presentation/ui/level_page.dart @@ -133,10 +133,7 @@ class LevelPage extends StatelessWidget { builder: (context, state) => Stack( clipBehavior: Clip.none, children: [ - BottomPath( - width: context.widthScreen * 0.75, - height: context.heightScreen * 0.65, - ), + BottomPath(), ...List.generate( context.read().bottom8LevelList.length, (index) => Positioned( diff --git a/lib/features/level/presentation/ui/widgets/bottom_path.dart b/lib/features/level/presentation/ui/widgets/bottom_path.dart index cb2d5a5..0d5e1b3 100644 --- a/lib/features/level/presentation/ui/widgets/bottom_path.dart +++ b/lib/features/level/presentation/ui/widgets/bottom_path.dart @@ -1,34 +1,28 @@ import 'package:flutter/material.dart'; +import 'package:hadi_hoda_flutter/common_ui/resources/my_colors.dart'; +import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; import 'package:path_drawing/path_drawing.dart'; class BottomPath extends StatelessWidget { - const BottomPath({ - super.key, - this.width = 500, - this.height = 1523, - this.color = Colors.white, - }); + const BottomPath({super.key}); - final double width; - final double height; - final Color color; @override Widget build(BuildContext context) { return CustomPaint( - painter: _Path(color), + painter: _Path(), size: Size( - width, - height, + context.widthScreen * 0.76, + context.heightScreen * 0.64, ), // or Size.infinite inside a parent with constraints ); } } class _Path extends CustomPainter { - _Path(this.color); + const _Path(); - final Color color; + final Color color = MyColors.white; // SVG viewBox static const double _vbW = 500; From ff154997a5c4094121b9072f428f5c959c55ef60 Mon Sep 17 00:00:00 2001 From: AmirrezaChegini Date: Sat, 11 Oct 2025 15:46:04 +0330 Subject: [PATCH 05/10] fix: reverse scroll level page --- lib/features/level/presentation/bloc/level_bloc.dart | 2 +- lib/features/level/presentation/ui/level_page.dart | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/features/level/presentation/bloc/level_bloc.dart b/lib/features/level/presentation/bloc/level_bloc.dart index 1fcf20e..c8b3961 100644 --- a/lib/features/level/presentation/bloc/level_bloc.dart +++ b/lib/features/level/presentation/bloc/level_bloc.dart @@ -148,7 +148,7 @@ class LevelBloc extends Bloc { await Future.delayed(const Duration(seconds: 1)); if (scrollController.hasClients) { - if(currentLevel < 9){ + if(currentLevel > 14){ scrollController.animateTo( scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 500), // Note: 500 seconds is very long. diff --git a/lib/features/level/presentation/ui/level_page.dart b/lib/features/level/presentation/ui/level_page.dart index edf3b25..482cbbe 100644 --- a/lib/features/level/presentation/ui/level_page.dart +++ b/lib/features/level/presentation/ui/level_page.dart @@ -22,6 +22,7 @@ class LevelPage extends StatelessWidget { children: [ SingleChildScrollView( controller: context.read().scrollController, + reverse: true, child: Stack( alignment: Alignment.center, children: [ From 83fdeb9499c01cd44a89867beff5ec14363f5fbe Mon Sep 17 00:00:00 2001 From: AmirrezaChegini Date: Sat, 11 Oct 2025 16:05:02 +0330 Subject: [PATCH 06/10] fix: animation and safe area in question page --- .../presentation/ui/question_page.dart | 1 + .../ui/screens/question_screen.dart | 42 ++++++++++++------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/lib/features/question/presentation/ui/question_page.dart b/lib/features/question/presentation/ui/question_page.dart index 746c25c..34b97ec 100644 --- a/lib/features/question/presentation/ui/question_page.dart +++ b/lib/features/question/presentation/ui/question_page.dart @@ -44,6 +44,7 @@ class QuestionPage extends StatelessWidget { ), ), child: SafeArea( + bottom: false, child: Padding( padding: const EdgeInsets.symmetric(horizontal: MySpaces.s16), child: Column( diff --git a/lib/features/question/presentation/ui/screens/question_screen.dart b/lib/features/question/presentation/ui/screens/question_screen.dart index 6d92073..acd10d9 100644 --- a/lib/features/question/presentation/ui/screens/question_screen.dart +++ b/lib/features/question/presentation/ui/screens/question_screen.dart @@ -116,25 +116,39 @@ class QuestionScreen extends StatelessWidget { builder: (context, state) => Stack( clipBehavior: Clip.none, children: [ - if(state.correctAnswer == false) - PositionedDirectional( - start: -100, - top: -10, + PositionedDirectional( + start: -100, + top: -10, + child: AnimatedOpacity( + opacity: state.correctAnswer == false ? 1 : 0, + duration: Duration(milliseconds: 200), child: LeftBlob(), ), - MyImage( - image: state.correctAnswer == true - ? MyAssets.happyPersons - : MyAssets.persons, - fit: BoxFit.contain, - size: 110, ), - if(state.correctAnswer == false) - PositionedDirectional( - top: -30, - end: -90, + AnimatedCrossFade( + duration: Duration(milliseconds: 200), + reverseDuration: Duration(milliseconds: 200), + crossFadeState: state.correctAnswer == true ? CrossFadeState + .showSecond : CrossFadeState.showFirst, + firstChild: MyImage( + image: MyAssets.persons, + fit: BoxFit.contain, + ), + secondChild: MyImage( + image: MyAssets.happyPersons, + fit: BoxFit.contain, + size: 110, + ), + ), + PositionedDirectional( + top: -30, + end: -90, + child: AnimatedOpacity( + opacity: state.correctAnswer == false ? 1 : 0, + duration: Duration(milliseconds: 200), child: RightBlob(), ), + ), ], ), ), From 00b64656e18ee8707430b832dce3bca800682452 Mon Sep 17 00:00:00 2001 From: AmirrezaChegini Date: Sat, 11 Oct 2025 16:07:41 +0330 Subject: [PATCH 07/10] fix: increase gravity of confetti --- lib/core/widgets/confetti/my_confetti.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/core/widgets/confetti/my_confetti.dart b/lib/core/widgets/confetti/my_confetti.dart index 1a101f9..a5f0486 100644 --- a/lib/core/widgets/confetti/my_confetti.dart +++ b/lib/core/widgets/confetti/my_confetti.dart @@ -15,9 +15,7 @@ class MyConfetti extends StatelessWidget { shouldLoop: false, blastDirectionality: BlastDirectionality.explosive, blastDirection: pi / 2, - // ↓ down - gravity: 0.2, - // let pieces fall + gravity: 1, emissionFrequency: 0.5, numberOfParticles: 15, particleDrag: 0.1, From 07d1de565702acc813a1bec377e929aad48e8003 Mon Sep 17 00:00:00 2001 From: AmirrezaChegini Date: Sat, 11 Oct 2025 16:10:51 +0330 Subject: [PATCH 08/10] fix: bottom padding of diamond page --- .../ui/screens/diamond_screen.dart | 58 ++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/lib/features/question/presentation/ui/screens/diamond_screen.dart b/lib/features/question/presentation/ui/screens/diamond_screen.dart index dc66891..66e02a9 100644 --- a/lib/features/question/presentation/ui/screens/diamond_screen.dart +++ b/lib/features/question/presentation/ui/screens/diamond_screen.dart @@ -4,6 +4,7 @@ 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/resources/my_colors.dart'; +import 'package:hadi_hoda_flutter/common_ui/resources/my_spaces.dart'; import 'package:hadi_hoda_flutter/common_ui/resources/my_text_style.dart'; import 'package:hadi_hoda_flutter/core/utils/gap.dart'; import 'package:hadi_hoda_flutter/core/utils/my_localization.dart'; @@ -128,37 +129,42 @@ class DiamondScreen extends StatelessWidget { ); } - Row _btns(BuildContext context) { - return Row( - children: [ - Expanded( - child: InkWell( - onTap: () => context.read().goToLevelPage(context: context), - child: Stack( - alignment: Alignment.center, - children: [ - MyImage(image: MyAssets.button3, size: 84), - Positioned( - top: 10, - child: Text( - context.translate.view_map, - style: DinoKids.regular35.copyWith( - color: Color(0XFFD93D16), + Widget _btns(BuildContext context) { + return Padding( + padding: EdgeInsets.only( + bottom: MediaQuery.viewPaddingOf(context).bottom + MySpaces.s16, + ), + child: Row( + children: [ + Expanded( + child: InkWell( + onTap: () => context.read().goToLevelPage(context: context), + child: Stack( + alignment: Alignment.center, + children: [ + MyImage(image: MyAssets.button3, size: 84), + Positioned( + top: 10, + child: Text( + context.translate.view_map, + style: DinoKids.regular35.copyWith( + color: Color(0XFFD93D16), + ), ), ), - ), - ], + ], + ), ), ), - ), - Expanded( - child: MyButton( - onTap: () => context.read().add(GetNextLevelEvent()), - title: context.translate.go_next, - type: ButtonType.type2, + Expanded( + child: MyButton( + onTap: () => context.read().add(GetNextLevelEvent()), + title: context.translate.go_next, + type: ButtonType.type2, + ), ), - ), - ], + ], + ), ); } } From 715cd1bec606a3795c640dcf8050956d3c61a28b Mon Sep 17 00:00:00 2001 From: AmirrezaChegini Date: Sat, 11 Oct 2025 17:12:23 +0330 Subject: [PATCH 09/10] fix: select previous level --- .../level/presentation/bloc/level_bloc.dart | 10 ++++++- .../level/presentation/bloc/level_event.dart | 4 ++- .../level/presentation/ui/level_page.dart | 29 ++++++++++++++----- .../presentation/ui/widgets/level_widget.dart | 12 ++++---- .../presentation/bloc/question_bloc.dart | 14 +++++---- 5 files changed, 48 insertions(+), 21 deletions(-) diff --git a/lib/features/level/presentation/bloc/level_bloc.dart b/lib/features/level/presentation/bloc/level_bloc.dart index c8b3961..7ec2351 100644 --- a/lib/features/level/presentation/bloc/level_bloc.dart +++ b/lib/features/level/presentation/bloc/level_bloc.dart @@ -26,6 +26,7 @@ class LevelBloc extends Bloc { on(_getLevelListEvent); on(_setCurrentLevelEvent); on(_startScrollEvent); + on(_chooseLevelEvent); } @override @@ -39,7 +40,7 @@ class LevelBloc extends Bloc { /// ------------Variables------------ final List bottomLocationList = [ - LevelLocation(bottom: -30, left: 30, index: 1), + LevelLocation(bottom: -20, left: 30, index: 1), LevelLocation(bottom: 50, left: 100, index: 2), LevelLocation(bottom: 150, left: 60, index: 3), LevelLocation(bottom: 210, left: 110, index: 4), @@ -173,4 +174,11 @@ class LevelBloc extends Bloc { } add(GetLevelListEvent()); } + + FutureOr _chooseLevelEvent(ChooseLevelEvent event, + Emitter emit,) { + if (event.type != LevelType.unFinished) { + emit(state.copyWith(chooseLevel: event.level)); + } + } } diff --git a/lib/features/level/presentation/bloc/level_event.dart b/lib/features/level/presentation/bloc/level_event.dart index 88f0cca..818ed6f 100644 --- a/lib/features/level/presentation/bloc/level_event.dart +++ b/lib/features/level/presentation/bloc/level_event.dart @@ -1,4 +1,5 @@ import 'package:hadi_hoda_flutter/features/level/domain/entity/level_entity.dart'; +import 'package:hadi_hoda_flutter/features/level/presentation/ui/widgets/level_widget.dart'; sealed class LevelEvent { const LevelEvent(); @@ -9,5 +10,6 @@ class StartScrollEvent extends LevelEvent {} class SetCurrentLevelEvent extends LevelEvent {} class ChooseLevelEvent extends LevelEvent { final LevelEntity level; - const ChooseLevelEvent(this.level); + final LevelType type; + const ChooseLevelEvent(this.level, this.type); } diff --git a/lib/features/level/presentation/ui/level_page.dart b/lib/features/level/presentation/ui/level_page.dart index 482cbbe..f0500ed 100644 --- a/lib/features/level/presentation/ui/level_page.dart +++ b/lib/features/level/presentation/ui/level_page.dart @@ -6,6 +6,7 @@ import 'package:hadi_hoda_flutter/core/utils/screen_size.dart'; import 'package:hadi_hoda_flutter/core/widgets/images/my_image.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_event.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/hint_level_widget.dart'; @@ -113,10 +114,15 @@ class LevelPage extends StatelessWidget { bottom: context.read().topLocationList[index].bottom, right: context.read().topLocationList[index].right, left: context.read().topLocationList[index].left, - child: LevelWidget( - level: context.read().top12LevelList[index], - type: context.read().getLevelType(index + 9), - onTap: (LevelEntity level) {}, + child: BlocBuilder( + builder: (context, state) => LevelWidget( + chooseLevel: state.chooseLevel, + level: context.read().top12LevelList[index], + type: context.read().getLevelType(index + 9), + onTap: (LevelEntity level, LevelType type) { + context.read().add(ChooseLevelEvent(level, type)); + }, + ), ), ), ), @@ -142,10 +148,17 @@ class LevelPage extends StatelessWidget { bottom: context.read().bottomLocationList[index].bottom, right: context.read().bottomLocationList[index].right, left: context.read().bottomLocationList[index].left, - child: LevelWidget( - level: context.read().bottom8LevelList[index], - type: context.read().getLevelType(index + 1), - onTap: (LevelEntity level) {}, + child: BlocBuilder( + buildWhen: (previous, current) => + previous.chooseLevel?.id != current.chooseLevel?.id, + builder: (context, state) => LevelWidget( + chooseLevel: state.chooseLevel, + level: context.read().bottom8LevelList[index], + type: context.read().getLevelType(index + 1), + onTap: (LevelEntity level, LevelType type) { + context.read().add(ChooseLevelEvent(level, type)); + }, + ), ), ), ), diff --git a/lib/features/level/presentation/ui/widgets/level_widget.dart b/lib/features/level/presentation/ui/widgets/level_widget.dart index 2850a82..c92a669 100644 --- a/lib/features/level/presentation/ui/widgets/level_widget.dart +++ b/lib/features/level/presentation/ui/widgets/level_widget.dart @@ -21,18 +21,20 @@ class LevelWidget extends StatelessWidget { const LevelWidget({ super.key, required this.level, - this.type, + required this.type, + required this.chooseLevel, this.onTap, }); - final LevelType? type; + final LevelType type; final LevelEntity level; - final Function(LevelEntity level)? onTap; + final LevelEntity? chooseLevel; + final Function(LevelEntity level, LevelType type)? onTap; @override Widget build(BuildContext context) { return InkWell( - onTap: () => onTap?.call(level), + onTap: () => onTap?.call(level, type), child: Stack( alignment: Alignment.topCenter, clipBehavior: Clip.none, @@ -51,7 +53,7 @@ class LevelWidget extends StatelessWidget { ], ), ), - if(type == LevelType.current) + if(level.id == chooseLevel?.id) Positioned( top: -20, child: MyImage( diff --git a/lib/features/question/presentation/bloc/question_bloc.dart b/lib/features/question/presentation/bloc/question_bloc.dart index e33c3bb..eda94c4 100644 --- a/lib/features/question/presentation/bloc/question_bloc.dart +++ b/lib/features/question/presentation/bloc/question_bloc.dart @@ -126,13 +126,15 @@ class QuestionBloc extends Bloc { ); if (state.currentQuestion?.order == state.levelEntity?.questions?.length) { - int currentLevel = int.parse( + int currentLevel = int.parse( LocalStorage.readData(key: MyConstants.currentLevel) ?? '1'); - ++currentLevel; - await LocalStorage.saveData( - key: MyConstants.currentLevel, - value: '$currentLevel', - ); + if (state.levelEntity?.order == currentLevel) { + ++currentLevel; + await LocalStorage.saveData( + key: MyConstants.currentLevel, + value: '$currentLevel', + ); + } } else { await playVoice(); } From c8e83be8c9411479d7195ddda1f86e35c504997f Mon Sep 17 00:00:00 2001 From: AmirrezaChegini Date: Sat, 11 Oct 2025 17:16:18 +0330 Subject: [PATCH 10/10] fix: theme --- lib/common_ui/theme/my_theme.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/common_ui/theme/my_theme.dart b/lib/common_ui/theme/my_theme.dart index 1223ae8..c0f9552 100644 --- a/lib/common_ui/theme/my_theme.dart +++ b/lib/common_ui/theme/my_theme.dart @@ -8,9 +8,9 @@ class MyTheme { const MyTheme._internal(); factory MyTheme() => _i; - static final ThemeData light = ThemeData(brightness: Brightness.light); + static final ThemeData light = ThemeData(brightness: Brightness.dark); - static final ThemeData dark = ThemeData(brightness: Brightness.dark); + static final ThemeData dark = ThemeData(brightness: Brightness.light); static Map get lightColors => { ColorsName.primaryColor: MyColors.white,