diff --git a/assets/images/loading.png b/assets/images/loading.png new file mode 100644 index 0000000..cf91972 Binary files /dev/null and b/assets/images/loading.png differ 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