Browse Source

fix: download flow

pull/20/head
AmirrezaChegini 3 weeks ago
parent
commit
789f74b93d
  1. BIN
      assets/images/loading.png
  2. 2
      lib/core/constants/my_constants.dart
  3. 23
      lib/core/middlewares/my_middlewares.dart
  4. 12
      lib/core/routers/my_routes.dart
  5. 50
      lib/core/widgets/animations/rotation_anim.dart
  6. 59
      lib/features/download/data/datasource/download_datasource.dart
  7. 16
      lib/features/download/data/repository_impl/download_repository_impl.dart
  8. 1
      lib/features/download/domain/repository/download_repository.dart
  9. 4
      lib/features/download/domain/usecases/get_images_usecase.dart
  10. 4
      lib/features/download/domain/usecases/loading_stream_usecase.dart
  11. 31
      lib/features/download/presentation/bloc/download_bloc.dart
  12. 1
      lib/features/download/presentation/bloc/download_event.dart
  13. 15
      lib/features/download/presentation/ui/download_page.dart
  14. 16
      lib/features/home/presentation/bloc/home_bloc.dart
  15. 78
      lib/features/intro/data/datasource/intro_datasource.dart
  16. 34
      lib/features/intro/data/repository_impl/intro_repository_impl.dart
  17. 9
      lib/features/intro/domain/repository/intro_repository.dart
  18. 12
      lib/features/intro/domain/usecases/loading_stream_usecase.dart
  19. 1
      lib/features/intro/presentation/bloc/intro_event.dart
  20. 136
      lib/features/intro/presentation/ui/widgets/intro_loading_widget.dart
  21. 1
      lib/features/language/presentation/bloc/language_bloc.dart
  22. 15
      lib/init_bindings.dart

BIN
assets/images/loading.png

After

Width: 156  |  Height: 174  |  Size: 8.2 KiB

2
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';
}

23
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<String?> intro(BuildContext context, GoRouterState state) {
final String? firstDownload = LocalStorage.readData(
key: MyConstants.firstDownload);
if (firstDownload != 'true') {
return Routes.languagePage;
} else {
return null;
}
}
}

12
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(),
),
),

50
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<RotationAnim> createState() => _RotationAnimState();
}
class _RotationAnimState extends State<RotationAnim>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 20),
);
_animation = Tween<double>(
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,
),
);
}
}

59
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<void> getImages();
Future<void> getAudios();
Future<void> saveLevels();
Stream<DownloadEntity> loadingStream();
@ -27,6 +28,54 @@ class DownloadDatasourceImpl implements IDownloadDatasource {
DownloadDatasourceImpl(this.httpRequest);
@override
Future<void> 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<void> 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));
}
}

16
lib/features/download/data/repository_impl/download_repository_impl.dart

@ -11,6 +11,22 @@ class DownloadRepositoryImpl implements IDownloadRepository {
const DownloadRepositoryImpl(this.datasource);
@override
Future<DataState<NoParams, MyException>> 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<DataState<NoParams, MyException>> getAudios() async {
try {

1
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<DataState<NoParams, MyException>> getImages();
Future<DataState<NoParams, MyException>> getAudios();
Future<DataState<NoParams, MyException>> saveLevels();
Stream<DownloadEntity> loadingStream();

4
lib/features/intro/domain/usecases/get_images_usecase.dart → 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<NoParams, NoParams> {
final IIntroRepository repository;
final IDownloadRepository repository;
const GetImagesUseCase(this.repository);

4
lib/features/download/domain/usecases/loading_stream_audio_usecase.dart → 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<DownloadEntity> call() {
return repository.loadingStream();

31
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<DownloadEvent, DownloadState> {
/// ------------constructor------------
DownloadBloc(
this._getImagesUseCase,
this._getAudiosUseCase,
this._loadingStreamUseCase,
this._saveLevelsUseCase,
)
: super(const DownloadState()) {
on<GetImagesEvent>(_getImagesEvent);
on<GetAudiosEvent>(_getAudiosEvent);
on<SaveLevelsEvent>(_saveLevelsEvent);
loadingStream = _loadingStreamUseCase();
}
/// ------------UseCases------------
final GetImagesUseCase _getImagesUseCase;
final GetAudiosUseCase _getAudiosUseCase;
final SaveLevelsUseCase _saveLevelsUseCase;
final LoadingStreamAudioUseCase _loadingStreamUseCase;
final LoadingStreamUseCase _loadingStreamUseCase;
/// ------------Variables------------
Stream<DownloadEntity> loadingStream = Stream.empty();
@ -39,6 +45,22 @@ class DownloadBloc extends Bloc<DownloadEvent, DownloadState> {
/// ------------Functions------------
/// ------------Api Calls------------
FutureOr<void> _getImagesEvent(
GetImagesEvent event,
Emitter<DownloadState> emit,
) async {
await _getImagesUseCase(NoParams()).then((value) {
value.fold(
(data) {
add(GetAudiosEvent());
},
(error) async {
emit(state.copyWith(getFilesStatus: BaseError(error.errorMessage)));
},
);
});
}
FutureOr<void> _getAudiosEvent(
GetAudiosEvent event,
Emitter<DownloadState> emit,
@ -63,7 +85,10 @@ class DownloadBloc extends Bloc<DownloadEvent, DownloadState> {
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)));

1
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 {}

15
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<DownloadPage> createState() => _DownloadPageState();
}
class _DownloadPageState extends State<DownloadPage> {
@override
void initState() {
super.initState();
context.read<DownloadBloc>().add(GetAudiosEvent());
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -66,7 +55,7 @@ class _DownloadPageState extends State<DownloadPage> {
horizontal: 60,
),
child: ErrorState(
onTap: () => context.read<DownloadBloc>().add(GetAudiosEvent()),
onTap: () => context.read<DownloadBloc>().add(GetImagesEvent()),
),
);
} else {

16
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<HomeEvent, HomeState> {
/// ------------constructor------------
@ -28,7 +32,17 @@ class HomeBloc extends Bloc<HomeEvent, HomeState> {
/// ------------Functions------------
void goToLevelPage(BuildContext context){
context.pushNamed(Routes.levelPage);
final String? selectedLanguage = LocalStorage.readData(key: MyConstants.selectLanguage);
final Box<TotalDataEntity> 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){

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

@ -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<void> getImages();
Stream<DownloadEntity> loadingStream();
}
class IntroDatasourceImpl implements IIntroDatasource {
final IHttpRequest httpRequest;
final StreamController<DownloadEntity> streamController = StreamController<DownloadEntity>.broadcast();
IntroDatasourceImpl(this.httpRequest);
@override
Future<void> 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<DownloadEntity> loadingStream() => streamController.stream;
}

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

@ -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<DataState<NoParams, MyException>> 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<DownloadEntity> loadingStream() {
return datasource.loadingStream();
}
}

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

@ -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<DataState<NoParams, MyException>> getImages();
Stream<DownloadEntity> loadingStream();
}

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

@ -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<DownloadEntity> call() {
return repository.loadingStream();
}
}

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

@ -1,4 +1,3 @@
sealed class IntroEvent {
const IntroEvent();
}
class GetImagesEvent extends IntroEvent {}

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

@ -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<DownloadEntity>? 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<DownloadEntity>(
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<Path> {
@override
Path getClip(Size size) {
// Original SVG viewBox: 334 x 60
const double w0 = 334.0;
const double h0 = 60.0;
final sx = size.width / w0;
final sy = size.height / h0;
// SVG path:
// M9.82057 10.3597
// C -1.70838 17.1589 -3.47995 44.4301 6.60447 53.1719
// C 16.0075 61.291 305.076 61.9385 323.201 53.4956
// C 341.326 45.0527 332.116 8.04571 324.829 5.7273
// C 307.985 -2.06805 28.6539 -0.77294 9.82057 10.3597
// Z
final p = Path()
..moveTo(9.82057 * sx, 10.3597 * sy)
..cubicTo(
-1.70838 * sx, 17.1589 * sy,
-3.47995 * sx, 44.4301 * sy,
6.60447 * sx, 53.1719 * sy,
)
..cubicTo(
16.0075 * sx, 61.291 * sy,
305.076 * sx, 61.9385 * sy,
323.201 * sx, 53.4956 * sy,
)
..cubicTo(
341.326 * sx, 45.0527 * sy,
332.116 * sx, 8.04571 * sy,
324.829 * sx, 5.7273 * sy,
)
..cubicTo(
307.985 * sx, -2.06805 * sy,
28.6539 * sx, -0.77294 * sy,
9.82057 * sx, 10.3597 * sy,
)
..close();
return p;
}
@override
bool shouldReclip(covariant CustomClipper<Path> oldClipper) => false;
}

1
lib/features/language/presentation/bloc/language_bloc.dart

@ -49,7 +49,6 @@ class LanguageBloc extends Bloc<LanguageEvent, LanguageState> {
key: MyConstants.selectLanguage,
value: state.selectedLang.code ?? 'fa',
),
LocalStorage.saveData(key: MyConstants.firstLanguagePage, value: 'true')
]);
if (Directory(

15
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>(() => GetSampleUseCase(locator()));
/// Intro Feature
locator.registerLazySingleton<IIntroDatasource>(() => IntroDatasourceImpl(locator()));
locator.registerLazySingleton<IIntroRepository>(() => IntroRepositoryImpl(locator()));
locator.registerLazySingleton<GetImagesUseCase>(() => GetImagesUseCase(locator()));
locator.registerLazySingleton<LoadingStreamUseCase>(() => LoadingStreamUseCase(locator()));
/// Download Feature
locator.registerLazySingleton<IDownloadDatasource>(() => DownloadDatasourceImpl(locator()));
locator.registerLazySingleton<IDownloadRepository>(() => DownloadRepositoryImpl(locator()));
locator.registerLazySingleton<GetImagesUseCase>(() => GetImagesUseCase(locator()));
locator.registerLazySingleton<GetAudiosUseCase>(() => GetAudiosUseCase(locator()));
locator.registerLazySingleton<SaveLevelsUseCase>(() => SaveLevelsUseCase(locator()));
locator.registerLazySingleton<LoadingStreamAudioUseCase>(() => LoadingStreamAudioUseCase(locator()));
locator.registerLazySingleton<LoadingStreamUseCase>(() => LoadingStreamUseCase(locator()));
/// Language Feature

Loading…
Cancel
Save